{"id":443024,"date":"2024-12-26T04:14:28","date_gmt":"2024-12-26T04:14:28","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=443024"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2024-12-26T04:14:28","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=443024","title":{"rendered":"<span>\u041f\u0438\u0448\u0435\u043c \u0441\u0432\u043e\u0439 PyTorch \u043d\u0430 NumPy. \u0427\u0430\u0441\u0442\u044c 2. \u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043d\u043e\u0432\u044b\u0435 \u0441\u043b\u043e\u0438<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p><strong>PyTorch<\/strong>\u00a0\u2014 \u044d\u0442\u043e \u043c\u043e\u0449\u043d\u044b\u0439 \u0438 \u0433\u0438\u0431\u043a\u0438\u0439 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f \u043c\u0430\u0448\u0438\u043d\u043d\u043e\u0433\u043e \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f, \u0448\u0438\u0440\u043e\u043a\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0439 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u0435\u0439\u0440\u043e\u043d\u043d\u044b\u0445 \u0441\u0435\u0442\u0435\u0439. \u041e\u043d \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u0435\u043d \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f, \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u0433\u0440\u0430\u0444\u0430\u043c \u0438 \u0431\u043e\u0433\u0430\u0442\u043e\u0439 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0434\u043b\u044f \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f \u043c\u043e\u0434\u0435\u043b\u0435\u0439. \u0414\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430, \u0447\u0430\u0441\u0442\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u043e\u0432\u0435\u0440\u0445\u043d\u043e\u0441\u0442\u043d\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u043e\u0432 \u043c\u0430\u0448\u0438\u043d\u043d\u043e\u0433\u043e \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f.<\/p>\n<p>\u041d\u043e \u0410\u043d\u0434\u0440\u0435\u0439 \u041a\u0430\u0440\u043f\u0430\u0442\u044b, \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0439 \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432 \u043e\u0431\u043b\u0430\u0441\u0442\u0438 \u0418\u0418, \u0441\u0447\u0438\u0442\u0430\u0435\u0442, \u0447\u0442\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u043e\u0432 \u0441 \u043d\u0443\u043b\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u043d\u044f\u0442\u044c \u0438\u0445 \u0441\u0443\u0442\u044c \u0438 \u0434\u0435\u0442\u0430\u043b\u0438 \u0440\u0430\u0431\u043e\u0442\u044b, \u0447\u0442\u043e \u0441\u043b\u043e\u0436\u043d\u043e \u043e\u0441\u043e\u0437\u043d\u0430\u0442\u044c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438. \u042d\u0442\u043e \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0440\u0430\u0437\u0432\u0438\u0442\u044c \u0438\u043d\u0442\u0443\u0438\u0446\u0438\u044e \u0434\u043b\u044f \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0438 \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f \u043c\u0435\u0442\u043e\u0434\u043e\u0432. \u0410\u043d\u0434\u0440\u0435\u0439 \u043f\u043e\u0441\u0432\u044f\u0449\u0430\u0435\u0442 \u043c\u043d\u043e\u0433\u043e \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u0447\u0442\u043e\u0431\u044b \u043e\u0431\u044a\u044f\u0441\u043d\u044f\u0442\u044c \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u044b \u0440\u0430\u0431\u043e\u0442\u044b \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0435\u0439 \u0432 \u0441\u0432\u043e\u0438\u0445\u00a0<a href=\"https:\/\/karpathy.ai\/\" rel=\"noopener noreferrer nofollow\">\u0431\u043b\u043e\u0433\u0430\u0445<\/a>\u00a0\u0438 \u043d\u0430 \u0441\u0432\u043e\u0451\u043c\u00a0<a href=\"https:\/\/www.youtube.com\/@AndrejKarpathy\" rel=\"noopener noreferrer nofollow\">\u044e\u0442\u0443\u0431-\u043a\u0430\u043d\u0430\u043b\u0435<\/a>. \u041e\u043d \u0442\u0430\u043a\u0436\u0435 \u043d\u0435 \u0440\u0430\u0437 \u043f\u043e\u0434\u0447\u0435\u0440\u043a\u0438\u0432\u0430\u043b, \u0447\u0442\u043e \u043d\u0430 \u0435\u0433\u043e \u043a\u0443\u0440\u0441\u0435 \u0432\u00a0<a href=\"https:\/\/cs.stanford.edu\/people\/karpathy\/\" rel=\"noopener noreferrer nofollow\">C\u0442\u044d\u043d\u0444\u043e\u0440\u0434\u0435<\/a>\u00a0\u0435\u0441\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438 \u043f\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u043e\u0432, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0435 \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0435\u043d\u0438\u0435.<\/p>\n<p>\u042f \u0445\u043e\u0442\u0435\u043b \u0431\u044b \u043f\u043e\u0441\u0432\u044f\u0442\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u0443\u044e \u0441\u0442\u0430\u0442\u044c\u044e \u044d\u0442\u043e\u0439 \u0438\u0434\u0435\u0438, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043c\u043d\u0435 \u0441\u0430\u043c\u043e\u043c\u0443 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u043a\u043e\u043f\u0430\u0442\u044c\u0441\u044f \u0432 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u0430\u0445 \u0433\u043b\u0443\u0431\u043e\u043a\u043e\u0433\u043e \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f. \u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u0435 <a href=\"https:\/\/habr.com\/ru\/articles\/869118\/\" rel=\"noopener noreferrer nofollow\">\u043f\u0435\u0440\u0432\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438<\/a><\/p>\n<p>\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043c\u044b:<\/p>\n<ul>\n<li>\n<p>\u0434\u043e\u0431\u0430\u0432\u0438\u043c\u00a0<strong>CNN<\/strong>,\u00a0<strong>BatchNorm<\/strong>,\u00a0<strong>MaxPool<\/strong>,\u00a0<strong>MinPool<\/strong><\/p>\n<\/li>\n<li>\n<p>\u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c\u00a0<strong>RMSProp<\/strong>,\u00a0<strong>NaG<\/strong>,\u00a0<strong>Adam<\/strong><\/p>\n<\/li>\n<li>\n<p>\u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0432 loss-\u0444\u0443\u043d\u043a\u0446\u0438\u044e<\/p>\n<\/li>\n<li>\n<p>\u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u043e\u0432\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438<\/p>\n<\/li>\n<li>\n<p>\u043d\u0430\u043f\u0438\u0448\u0435\u043c\u00a0<strong>DataLoader<\/strong><\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e\u0435\u0445\u0430\u043b\u0438!<\/p>\n<p>\u041c\u044b \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0438\u0441\u044c \u043d\u0430 \u0442\u043e\u043c, \u0447\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u043e\u0445\u043e\u0436\u0443\u044e \u043d\u0430 <strong>PyTorch<\/strong> \u043e\u0431\u0435\u0440\u0442\u043a\u0443.<\/p>\n<pre><code class=\"python\">loss_fn = CrossEntropyLoss() model = SimpleNet() optim = SGD(model.parameters(), learning_rate = 0.01)  for i in range(100):     output = model(input_x)     loss = loss_fn(output, target_x)     loss.backward()     optim.step()<\/code><\/pre>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u043f\u044b\u0442\u0430\u0435\u043c\u0441\u044f \u043e\u0431\u0443\u0447\u0438\u0442\u044c \u043d\u0430 \u043a\u0430\u043a\u0438\u0445-\u043d\u0438\u0431\u0443\u0434\u044c \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445! \u0414\u043b\u044f \u044d\u0442\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u043d\u0430\u043c \u043f\u043e\u0434\u043e\u0439\u0434\u0435\u0442 \u043d\u0430\u0431\u043e\u0440 \u0440\u0443\u043a\u043e\u043f\u0438\u0441\u043d\u044b\u0445 \u0446\u0438\u0444\u0440 MNIST. \u041d\u043e \u043a\u0430\u043a \u043d\u0430\u043c \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432\u0435\u0441\u044c \u0434\u0430\u0442\u0430\u0441\u0435\u0442? \u0412\u0441\u043f\u043e\u043c\u043d\u0438\u043c \u043a\u0430\u043a \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u0432 <strong>PyTorch<\/strong>.  \u041c\u044b \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u043a\u043b\u0430\u0441\u0441\u0430 <strong>DataLoader<\/strong> &#8212; \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0440\u0430\u0437\u0431\u0438\u0442\u044c \u0432\u0435\u0441\u044c \u0434\u0430\u0442\u0430\u0441\u0435\u0442 \u043d\u0430 \u0431\u0430\u0442\u0447\u0438 \u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043a\u0430\u043a \u0438\u0442\u0435\u0440\u0438\u0440\u0443\u0435\u043c\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u043d\u0430\u0448 \u043d\u0430\u0431\u043e\u0440 \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 <code>dataset<\/code>.  \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440<\/p>\n<pre><code class=\"python\">import pandas as pd import numpy as np data_pd = pd.read_csv(path) dataset = np.array(data_pd).astype(float) np.random.shuffle(dataset) # \u043f\u0435\u0440\u0435\u043c\u0435\u0448\u0430\u0435\u043c \u0434\u0430\u0442\u0430\u0441\u0435\u0442 dataset.shape &gt;&gt;&gt; (5000, 785) # 1 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a\u043b\u0430\u0441\u0441\u0430 + 784 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u0438\u0441\u043a\u0435\u043b\u0435\u0439<\/code><\/pre>\n<h4>Dataloader<\/h4>\n<pre><code class=\"python\">class DataLoader():     def __init__(self, data, batch_size=64, shuffle=True, flatten = True):         self.data = data         self.index = 0 # \u0438\u043d\u0434\u0435\u043a\u0441 \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0438         self.items = [] # \u0437\u0434\u0435\u0441\u044c \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043d\u0430\u0431\u043e\u0440\u044b \u0431\u0430\u0442\u0447\u0435\u0439         self.flatten = flatten  # \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437 \u0441\u043c\u043e\u0436\u0435\u043c \u043f\u0440\u043e\u0438\u0442\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u044c \u043d\u0430\u0431\u043e\u0440         self.max = data.shape[0] \/\/ batch_size + (1 if data.shape[0] % batch_size != 0 else 0)          if shuffle == True:         # \u043c\u0435\u0448\u0430\u0435\u043c \u043d\u0430\u0431\u043e\u0440 \u0435\u0441\u043b\u0438 \u0437\u0430\u0445\u043e\u0442\u0435\u043b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c             self.data = np.random.permutation(self.data) # \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u0441\u0435\u0445 \u0431\u0430\u0442\u0447\u0435\u0439         for _ in range(self.max):             self.items.append(self.data[batch_size * _: batch_size * (_ + 1)])                  # \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0432 \u0438\u0442\u0435\u0440\u0438\u0440\u0443\u0435\u043c\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 def __iter__(self):         return self      # \u0434\u043b\u044f \u0446\u0438\u043a\u043b\u043e\u0432 for \u0438 while     def __next__(self):         if self.index &lt; self.max:             value = self.items[self.index] # \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0431\u0430\u0442\u0447             self.index += 1             if self.flatten:                  # \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043b\u0438\u0431\u043e ((64, 784), (64, 1))                 return value[:, 1:], value[:, 0].reshape(-1, 1)             else:         # \u043b\u0438\u0431\u043e ((64, 28, 28), (64, 1)), \u0442\u043e \u0435\u0441\u0442\u044c \u043a\u0430\u043a \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435                 return value[:, 1:].reshape(value.shape[0], 28, 28), value[:, 0].reshape(-1, 1)         else:             self.index = 0             raise StopIteration # \u0432\u044b\u0445\u043e\u0434\u0438\u043c \u0438\u0437 \u0446\u0438\u043a\u043b\u0430<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432\u0441\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043d\u0430\u0431\u043e\u0440\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e \u0431\u0430\u0442\u0447\u0430\u043c \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"python\">data_loader = DataLoader(dataset, batch_size=64, shuffle=True, flatten=True)<\/code><\/pre>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u043d\u0430\u0448 \u0431\u0430\u0442\u0447.<\/p>\n<pre><code class=\"python\">for x, target in dataloader: break target.shape &gt;&gt;&gt; (64, 1)<\/code><\/pre>\n<p>\u0421\u0442\u0440\u0430\u043d\u043d\u043e, \u0443 \u043d\u0430\u0441 \u0432\u0440\u043e\u0434\u0435 10 \u043a\u043b\u0430\u0441\u0441\u043e\u0432, \u0430 \u0440\u0430\u0437\u043c\u0435\u0440 \u043f\u043e\u0447\u0435\u043c\u0443-\u0442\u043e 1. \u0417\u0430\u0433\u043b\u044f\u043d\u0435\u043c \u0432\u043d\u0443\u0442\u0440\u044c<\/p>\n<pre><code class=\"python\">target[0] &gt;&gt;&gt; array([8.])<\/code><\/pre>\n<p>\u042d\u0442\u043e \u043d\u043e\u043c\u0435\u0440 \u043a\u043b\u0430\u0441\u0441\u0430, \u0430 \u043c\u044b \u0432\u0435\u0434\u044c \u0436\u0434\u0430\u043b\u0438, \u0447\u0442\u043e \u0431\u0443\u0434\u0435\u0442 <code>[0, 0, 0, 0, 0, 0, 0, 0, 1, 0]<\/code><br \/>\u0422\u043e\u0433\u0434\u0430 \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u0442\u0430\u043a\u043e\u0439 \u0441\u043b\u0443\u0447\u0430\u0439 \u0432\u043d\u0443\u0442\u0440\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 <code>CrossEntropyLoss<\/code><\/p>\n<pre><code class=\"python\">class CrossEntropyLoss:      def __call__(self, logits, true):         predicted = np.exp(logits) \/ np.sum(np.exp(logits), axis=1).reshape(-1, 1) # softmax         self.predicted = np.array(predicted, copy=True) # \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043a\u043e\u043f\u0438\u044e \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u043c\u0430\u0442\u0440\u0438\u0446\u044b \u0434\u043b\u044f \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0438\u0445 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0439         ### \u0414\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u044d\u0442\u0438 \u0441\u0442\u0440\u043e\u0447\u043a\u0438         number_of_classes = predicted.shape[1] self.true = np.int_(np.arange(0, number_of_classes) == true)  ###         # \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043b\u043e\u0441\u0441-\u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u0440\u044f\u043c\u043e \u043f\u043e \u0444\u043e\u0440\u043c\u0443\u043b\u0435         self.loss = -1 * np.sum(self.true * np.log(self.predicted + 1e-5), axis=1)         return self<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u043e\u0431\u0443\u0447\u0438\u0442\u044c \u043d\u0430\u0448\u0443 \u043d\u0435\u0439\u0440\u043e\u043d\u043a\u0443. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043c\u043e\u0434\u0435\u043b\u044c<\/p>\n<pre><code class=\"python\">class SimpleNet(Module):     def __init__(self):         super().__init__()         self.linear1 = Linear(input_channels=784, output_channels=200, bias=True)         self.linear2 = Linear(input_channels=200, output_channels=50, bias=True)         self.linear3 = Linear(input_channels=50, output_channels=10, bias=True)         self.relu = ReLU()      def forward(self, x):         x = self.linear1(x)         x = self.relu(x)         x = self.linear2(x)         x = self.relu(x)         x = self.linear3(x)         return x<\/code><\/pre>\n<p>\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u0432\u0441\u0451 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0435<\/p>\n<pre><code class=\"python\">loss_fn = CrossEntropyLoss() model = SimpleNet() optim = SGD(model.parameters(), learning_rate = 0.01)<\/code><\/pre>\n<p>\u0414\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043d\u0430\u0431\u043e\u0440\u0430 \u0432 5000 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u043e\u0432 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f <code>5000 \/\/ 64 = 78 \u0431\u0430\u0442\u0447\u0435\u0439<\/code>. \u041f\u0440\u043e\u0439\u0434\u0435\u043c\u0441\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, 5 \u0440\u0430\u0437 \u043f\u043e \u043d\u0430\u0448\u0435\u043c\u0443 \u0434\u0430\u0442\u0430\u0441\u0435\u0442\u0443, \u0442\u043e \u0435\u0441\u0442\u044c \u0443 \u043d\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 5 \u044d\u043f\u043e\u0445.<\/p>\n<pre><code class=\"python\">for i in range(5):     for batch in data_loader:         input_x, target = batch         input_x = input_x \/ 255 # \u043d\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u043d\u0430 [0, 1], \u0438\u043d\u0430\u0447\u0435 \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u0441\u0442\u0430\u0442\u044c \u043d\u0435\u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u044b\u043c         output = model(input_x)         loss = loss_fn(output, target)         loss.backward()         optim.step()     print(loss.loss.mean(), i) # \u0411\u0435\u0440\u0435\u043c \u0441\u0440\u0435\u0434\u043d\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043b\u043e\u0441\u0441\u0430 \u043f\u043e \u0431\u0430\u0442\u0447\u0443  &gt;&gt;&gt;0.622 0 0.294 1 0.166 2 0.101 3 0.070 4<\/code><\/pre>\n<p>\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <strong>loss<\/strong>-\u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0443\u043c\u0435\u043d\u044c\u0448\u0430\u0435\u0442\u0441\u044f, \u0437\u043d\u0430\u0447\u0438\u0442 \u043d\u0430\u0448\u0430 \u043c\u043e\u0434\u0435\u043b\u044c \u043e\u0431\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043d\u0430\u0448\u0435\u043c \u043d\u0430\u0431\u043e\u0440\u0435 \u0434\u0430\u043d\u043d\u044b\u0445!<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0439\u0434\u0435\u043c \u0434\u0430\u043b\u044c\u0448\u0435 \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u043e\u0432\u044b\u0435 \u0441\u043b\u043e\u0438! \u0412 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u044f \u0445\u043e\u0442\u0435\u043b \u0431\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0432\u0451\u0440\u0442\u043e\u0447\u043d\u044b\u0435 \u0441\u043b\u043e\u0438, \u0442\u0430\u043a \u043a\u0430\u043a \u0438\u043c\u0435\u043d\u043d\u043e \u043e\u043d\u0438 \u0445\u043e\u0440\u043e\u0448\u043e \u0441\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0441 \u0441\u043b\u043e\u0436\u043d\u044b\u043c\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u043c\u0438. \u0422\u043e\u043b\u044c\u043a\u043e \u043b\u0438\u0448\u044c \u043d\u0430 \u043b\u0438\u043d\u0435\u0439\u043d\u044b\u0445 \u0441\u043b\u043e\u044f\u0445 \u043c\u044b \u0434\u0430\u043b\u0435\u043a\u043e \u043d\u0435 \u0443\u0439\u0434\u0435\u043c.<\/p>\n<h2>CNN<\/h2>\n<p>\u041e\u043f\u0435\u0440\u0430\u0446\u0438\u044f \u0441\u0432\u0435\u0440\u0442\u043a\u0438 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0432 \u0441\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u043d\u0438\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0444\u0438\u043b\u044c\u0442\u0440\u0430.\u00a0<strong>\u0424\u0438\u043b\u044c\u0442\u0440<\/strong>\u00a0\u0438\u043b\u0438 <strong>kernel<\/strong> \u2014 \u044d\u0442\u043e \u0442\u043e\u0436\u0435 \u043c\u0430\u0442\u0440\u0438\u0446\u0430 \u0447\u0438\u0441\u0435\u043b.<br \/>\u0421\u0432\u0435\u0440\u0442\u043a\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0440\u0430\u0437\u043c\u0435\u0440\u0430 4\u04454 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u043c \u0440\u0430\u0437\u043c\u0435\u0440\u0430 3\u04453: \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f\u00a0<strong>\u043a\u0430\u0440\u0442\u043e\u0439 \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438<\/strong>. \u0421\u0432\u0435\u0440\u0442\u043a\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0448\u0430\u0433 \u0437\u0430 \u0448\u0430\u0433\u043e\u043c: <\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c23\/394\/ed2\/c23394ed25bc0e3d0a4118476c7a79a3.gif\" alt=\"Convolution\" width=\"751\" height=\"401\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c23\/394\/ed2\/c23394ed25bc0e3d0a4118476c7a79a3.gif\"\/><\/p>\n<div><figcaption>Convolution<\/figcaption><\/div>\n<\/figure>\n<p>\u0417\u0430\u043c\u0435\u0442\u0438\u043c, \u0447\u0442\u043e \u0440\u0430\u0437\u043c\u0435\u0440 \u043a\u0430\u0440\u0442\u044b \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438 \u043f\u043e\u0441\u043b\u0435 \u0441\u0432\u0435\u0440\u0442\u043a\u0438 \u0441\u0442\u0430\u043b \u043c\u0435\u043d\u044c\u0448\u0435, \u0447\u0435\u043c \u0440\u0430\u0437\u043c\u0435\u0440 \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f. \u041e\u0431\u0449\u0430\u044f \u0444\u043e\u0440\u043c\u0443\u043b\u0430 \u0440\u0430\u0437\u043c\u0435\u0440\u0430 \u043a\u0430\u0440\u0442\u044b \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438 \u0442\u0430\u043a\u0430\u044f:<\/p>\n<p><code>m = (i \u2212 f + 2 * padding) \/ stride + 1<\/code>,<br \/> \u0433\u0434\u0435:<\/p>\n<ul>\n<li>\n<p><code>m<\/code> \u2014 \u0440\u0430\u0437\u043c\u0435\u0440 \u043a\u0430\u0440\u0442\u044b \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438;<\/p>\n<\/li>\n<li>\n<p><code>i<\/code> \u2014 \u0440\u0430\u0437\u043c\u0435\u0440 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f;<\/p>\n<\/li>\n<li>\n<p><code>f<\/code> \u2014 \u0440\u0430\u0437\u043c\u0435\u0440 \u0444\u0438\u043b\u044c\u0442\u0440\u0430.<\/p>\n<\/li>\n<li>\n<p><code>padding<\/code> \u2014 \u043e\u0442\u0441\u0442\u0443\u043f \u043e\u0442 \u043a\u0440\u0430\u044f<\/p>\n<\/li>\n<li>\n<p><code>stride<\/code> \u2014 \u0448\u0430\u0433 \u0444\u0438\u043b\u044c\u0442\u0440\u0430<\/p>\n<\/li>\n<\/ul>\n<p>\u0420\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 <a href=\"https:\/\/colab.research.google.com\/drive\/1ZMu6C3ZEt3kCSDBNWGM6sicXL5-EhSve?usp=sharing#scrollTo=JpTe1vvMkTmG\" rel=\"noopener noreferrer nofollow\">\u0445\u043e\u0440\u043e\u0448\u0438\u043c \u043d\u043e\u0443\u0442\u0431\u0443\u043a\u043e\u043c<\/a>, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043d\u044f\u0442\u044c \u0438\u043d\u0442\u0443\u0438\u0446\u0438\u044e \u0441\u0432\u0451\u0440\u0442\u043e\u043a<\/p>\n<p>\u041a\u0430\u043a \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0441\u0432\u0451\u0440\u0442\u043e\u0447\u043d\u0430\u044f \u0441\u0435\u0442\u044c \u0434\u043b\u044f \u043a\u043b\u0430\u0441\u0441\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/83a\/2b5\/539\/83a2b5539a44045242f390a15db56f4c.png\" width=\"1164\" height=\"546\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/83a\/2b5\/539\/83a2b5539a44045242f390a15db56f4c.png\"\/><\/figure>\n<p>\u0423 \u043d\u0430\u0441 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0441\u043b\u043e\u0438 <code>Flatten<\/code> \u0438 <code>Linear<\/code>. \u041e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e <code>Conv2d<\/code>.<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u0443\u044e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u0441\u0432\u0435\u0440\u0442\u043a\u0438 \u043e\u0434\u043d\u043e\u0439 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u0444\u0438\u043b\u044c\u0442\u0440\u0430.<\/p>\n<pre><code class=\"python\">image = np.array([[0, 50, 0, 29],         [0, 80, 31, 2],          [33, 90, 0, 75],         [0, 9, 0, 95]         ]) kernel = np.ones((3, 3))  image, kernel &gt;&gt;&gt;array([[ 0, 50,  0, 29],         [ 0, 80, 31,  2],         [33, 90,  0, 75],         [ 0,  9,  0, 95]]),  array([[1., 1., 1.],         [1., 1., 1.],         [1., 1., 1.]])<\/code><\/pre>\n<pre><code class=\"python\">i = image.shape[0] f = kernel.shape[0] padding = 0 step = 1 m = (i - f + 2*padding) \/\/ step + 1 &gt;&gt;&gt; 2<\/code><\/pre>\n<pre><code class=\"python\">new_image = np.zeros((m, m))  for y in range(m):     for x in range(m):         start_x = x * step         end_x = start_x + f         start_y = y * step         end_y = start_y + f         new_image[y][x] = np.sum(image[start_y:end_y, start_x:end_x] * kernel) new_image &gt;&gt;&gt; array([[284., 357.],        [243., 382.]])<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u0441 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u0447\u043d\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439.<\/p>\n<pre><code class=\"python\">import scipy.signal scipy.signal.fftconvolve(image, kernel, mode='valid') &gt;&gt;&gt;array([[284., 357.],        [243., 382.]])<\/code><\/pre>\n<p>\u0412 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u0432\u0451\u0440\u0442\u043a\u0438 \u0438\u043c\u0435\u043d\u043d\u043e <code>scipy.signal.fftconvolve<\/code>, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u044d\u0442\u043e \u0432\u0435\u0441\u044c\u043c\u0430 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0435\u043c\u043a\u0430\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f, \u0430 \u0434\u0430\u043d\u043d\u044b\u0439 <a href=\"https:\/\/docs.scipy.org\/doc\/scipy\/reference\/generated\/scipy.signal.fftconvolve.html\" rel=\"noopener noreferrer nofollow\">\u043c\u0435\u0442\u043e\u0434<\/a> \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u0441\u0447\u0438\u0442\u0430\u0442\u044c \u0442\u043e\u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0424\u0443\u0440\u044c\u0435!<br \/>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0446\u0432\u0435\u0442\u043d\u0443\u044e \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443, \u0442\u043e \u0435\u0441\u0442\u044c \u043d\u0435 <code>(height, width)<\/code>, \u0430 <code>(channels, height, width)<\/code><br \/><em>\u0421\u043f\u043e\u0439\u043b\u0435\u0440<\/em>: \u0435\u0441\u0442\u044c \u0434\u0432\u0430 \u0441\u043f\u043e\u0441\u043e\u0431\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u044c \u0441\u0432\u0435\u0440\u0442\u043a\u0443 \u043c\u043d\u043e\u0433\u043e\u043a\u0430\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/cca\/911\/8d2\/cca9118d2aba7202e7a28de3ce98fb6d.png\" width=\"2044\" height=\"724\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/cca\/911\/8d2\/cca9118d2aba7202e7a28de3ce98fb6d.png\"\/><\/figure>\n<p>\u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0432\u044b\u0439 \u043c\u0435\u0442\u043e\u0434, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0447\u0430\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0439.<br \/>\u0421\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u0438 \u0444\u0438\u043b\u044c\u0442\u0440<\/p>\n<pre><code class=\"python\">image = np.random.randn(3, 7, 7) kernel = np.ones((3, 3, 3))<\/code><\/pre>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043f\u043e\u0447\u0442\u0438 \u043d\u0438\u043a\u0430\u043a \u043d\u0435 \u043f\u043e\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f<\/p>\n<pre><code class=\"python\">i = image.shape[1] f = kernel.shape[1] padding = 0 step = 1 m = (i-f + 2*padding) \/\/ step +1 new_image = np.zeros((m, m)) for y in range(m):     for x in range(m):         start_x = x * step         end_x = start_x + f         start_y = y * step         end_y = start_y + f         new_image[y][x] = np.sum(image[:, start_y:end_y, start_x:end_x] * kernel)<\/code><\/pre>\n<p>\u0421\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0435\u043c!<\/p>\n<pre><code class=\"python\">np.allclose(new_image, scipy.signal.fftconvolve(image, kernel, mode='valid')) &gt;&gt;&gt; True<\/code><\/pre>\n<p>\u041a\u0440\u0443\u0442\u043e!<br \/> \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u0441\u0432\u0451\u0440\u0442\u043a\u0438 \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430\u0445 \u043e\u0434\u043d\u0438\u043c \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u043c. \u041e\u043f\u044f\u0442\u044c \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0443\u0441\u043b\u043e\u0436\u043d\u0438\u043c \u043a\u043e\u0434<\/p>\n<pre><code class=\"python\">image = np.random.randn(10, 3, 7, 7) kernel = np.ones((1, 3, 3, 3))  i = image.shape[-1] f = kernel.shape[-1] padding = 0 step = 1 m = (i-f + 2*padding) \/\/ step +1 number_of_images = image.shape[0] new_image = np.zeros((number_of_images, m, m)) for image_n in range(number_of_images):     for y in range(m):         for x in range(m):             start_x = x * step             end_x = start_x + f             start_y = y * step             end_y = start_y + f             new_image[image_n][y][x] = np.sum(image[image_n,:, start_y:end_y, start_x:end_x] * kernel)<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c<\/p>\n<pre><code class=\"python\">np.allclose(new_image, scipy.signal.fftconvolve(image, kernel, mode='valid').squeeze(axis=1)) &gt;&gt;&gt; True<\/code><\/pre>\n<p>\u0423\u0440\u0430, \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c, \u0442\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u043e\u0431\u043e\u0440\u043e\u0442, \u043f\u0440\u043e\u0439\u0434\u0435\u043c\u0441\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u043c\u0438 \u043f\u043e \u043e\u0434\u043d\u043e\u0439 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0435!<\/p>\n<pre><code class=\"python\">image = np.random.randn(1, 3, 7, 7) kernel = np.ones((5, 3, 3, 3))  i = image.shape[-1] f = kernel.shape[-1] step = 1 m = (i-f) \/\/ step +1 number_of_kernels = kernel.shape[0] new_image = np.zeros((number_of_kernels, m, m)) for kernel_n in range(number_of_kernels):     for y in range(m):         for x in range(m):             start_x = x * step             end_x = start_x + f             start_y = y * step             end_y = start_y + f             new_image[kernel_n][y][x] = np.sum(image[0, :, start_y:end_y, start_x:end_x] * kernel[kernel_n])<\/code><\/pre>\n<p>\u0421\u043d\u043e\u0432\u0430 \u0441\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0435\u043c!<\/p>\n<pre><code class=\"python\">np.allclose(new_image, scipy.signal.fftconvolve(image, kernel, mode='valid').squeeze(axis=1)) &gt;&gt;&gt; True<\/code><\/pre>\n<p>\u042d\u0442\u043e \u043e\u0447\u0435\u043d\u044c \u043a\u0440\u0443\u0442\u043e! \u0410 \u0442\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043e\u0431\u0435\u0440\u0451\u043c \u044d\u0442\u043e \u0432\u0441\u0451 \u0438 \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u0441\u0432\u0435\u0440\u0442\u043a\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432 \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430\u0445!<\/p>\n<pre><code class=\"python\">image = np.random.randn(10, 1, 3, 7, 7) kernel = np.ones((1, 5, 3, 3, 3))  i = image.shape[-1] f = kernel.shape[-1] padding = 0 step = 1 m = (i-f + 2*padding) \/\/ step +1 number_of_kernels = kernel.shape[1] number_of_images = image.shape[0] new_image = np.zeros((number_of_images, number_of_kernels, m, m)) for image_n in range(number_of_images):     for kernel_n in range(number_of_kernels):         for y in range(m):             for x in range(m):                 start_x = x * step                 end_x = start_x + f                 start_y = y * step                 end_y = start_y + f                 new_image[image_n][kernel_n][y][x] = np.sum(image[image_n, 0, :, start_y:end_y, start_x:end_x] * kernel[0, kernel_n]) <\/code><\/pre>\n<pre><code class=\"python\">np.allclose(new_image, scipy.signal.fftconvolve(image, kernel, mode='valid').squeeze(axis=2)) &gt;&gt;&gt; True <\/code><\/pre>\n<p><strong>\u041e\u0447\u0435\u043d\u044c \u043a\u0440\u0443\u0442\u043e\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442!<\/strong> \u041c\u044b \u0441 \u0432\u0430\u043c\u0438 \u0441\u043c\u043e\u0433\u043b\u0438 \u043f\u043e\u043b\u043d\u043e\u0441\u0442\u044c\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043e\u0434\u043d\u0443 \u0438\u0437 \u0431\u0430\u0437\u043e\u0432\u044b\u0445 \u0438 \u0441\u0430\u043c\u044b\u0445 \u0432\u0430\u0436\u043d\u044b\u0445 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439 \u0432 \u043d\u0435\u0439\u0440\u043e\u043d\u043d\u044b\u0445 \u0441\u0435\u0442\u044f\u0445.<br \/>\u0412 <strong>PyTorch<\/strong> \u0437\u0430 \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u0441\u0432\u0435\u0440\u0442\u043a\u0438 \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0442 \u0441\u043b\u043e\u0438 <code>nn.Conv1d<\/code>, <code>nn.Conv2d<\/code>, <code>nn.Conv3d<\/code> &#8212; \u043b\u043e\u0433\u0438\u043a\u0430 \u0443 \u043d\u0438\u0445 \u0443 \u0432\u0441\u0435\u0445 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u0430\u044f, \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043e\u0442\u043b\u0438\u0447\u0430\u0435\u0442\u0441\u044f \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0430. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043c\u044b \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 <code>nn.Conv2d<\/code>, \u0435\u0441\u043b\u0438 \u0431\u044b \u043c\u044b \u0434\u0435\u043b\u0430\u043b\u0438 \u043c\u0435\u0442\u043e\u0434 <code>2<\/code> \u043d\u0430 \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0435\u0439 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0435, \u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u0438 \u0431\u044b <code>nn.Conv1d<\/code>. \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043e\u0431\u0435\u0440\u0435\u043c \u0432\u0441\u0435 \u0437\u043d\u0430\u043d\u0438\u044f \u0432 \u043e\u0434\u0438\u043d \u043a\u043b\u0430\u0441\u0441.<\/p>\n<p><strong>Note!<\/strong><br \/>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0443\u0441\u043b\u043e\u0432\u0438\u043c\u0441\u044f, \u0447\u0442\u043e \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430 \u0432 \u044d\u0442\u043e\u0442 \u0441\u043b\u043e\u0439 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0434\u0430\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u0432\u0438\u0434\u0435 <code>(batch, channels, height, width<\/code>)<br \/>\u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, 1 \u0447\u0435\u0440\u043d\u043e-\u0431\u0435\u043b\u0430\u044f \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0438\u043c\u0435\u0442\u044c \u0444\u043e\u0440\u043c\u0430\u0442 <code>(1, 1, 28, 28)<\/code><br \/>\u0422\u0430\u043a\u0436\u0435 \u0434\u043b\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0432 <strong>PyTorch<\/strong> \u043c\u043e\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f <code>padding<\/code> \u0438 <code>stride<\/code>.  \u041c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u0432\u0451\u0440\u0442\u043a\u0438 \u0443\u0447\u0438\u0442\u044b\u0432\u0430\u044f \u044d\u0442\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b, \u043d\u043e \u044f \u043f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u044d\u0442\u043e\u0433\u043e \u043d\u0435 \u0434\u0435\u043b\u0430\u0442\u044c, \u0442\u0430\u043a \u043a\u0430\u043a \u044d\u0442\u043e \u0437\u0430\u043c\u0435\u0442\u043d\u043e \u0443\u0441\u043b\u043e\u0436\u043d\u0438\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e!<br \/>\u0418\u0442\u0430\u043a, <code>padding = 0, stride=1<\/code>  \u0432\u0441\u0435\u0433\u0434\u0430<\/p>\n<h2>Conv2d<\/h2>\n<pre><code class=\"python\">class Conv2d:      def __init__(self, input_channels: int, output_channels: int, kernel_size: int, bias = True):         self.bias = bias         self.kernel_size = (output_channels, input_channels, kernel_size, kernel_size)         self.filter_array = np.random.randn(1, *self.kerne_size) * 0.1 # \u0434\u043e\u043c\u043d\u043e\u0436\u0430\u0435\u043c \u043d\u0430 0.1 \u0447\u0442\u043e\u0431\u044b \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0435 \u0432\u0435\u0441\u0430 \u0431\u044b\u043b\u0438 \u0441\u043e\u0432\u0441\u0435\u043c \u043c\u0430\u043b\u0435\u043d\u044c\u043a\u0438\u043c\u0438         self.bias = np.random.randn(1, output_channels, 1, 1) * 0.1         Parameter([self, self.filter_array, self.bias * bias])      def __call__(self, x):         self.x = np.expand_dims(x, axis=1) # \u0441\u043e\u0445\u0440\u0430\u043d\u044f\u0435\u043c \u043d\u0430 \u0432\u0441\u044f\u043a\u0438\u0439 \u0441\u043b\u0443\u0447\u0430\u0439 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435         # x: (batch, channels, height, width) - &gt; (batch, 1, channels, height, width)         return scipy.signal.fftconvolve(np.expand_dims(x, axis=1), self.filter_array, mode='valid').squeeze(axis=2) + self.bias <\/code><\/pre>\n<p>\u041a\u0430\u043a \u0432\u0441\u0451 \u043a\u0440\u0430\u0441\u0438\u0432\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c <code>scipy.signal.fftconvolve<\/code>, \u043d\u043e \u043c\u044b \u0441 \u0432\u0430\u043c\u0438 \u0438\u043c\u0435\u0435\u043c \u043f\u0440\u0430\u0432\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0442\u0430\u043a\u0443\u044e \u0437\u0430\u043c\u0435\u043d\u0443, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043c\u044b \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u043b\u0438\u0441\u044c \u0432 \u0434\u0435\u0442\u0430\u043b\u044f\u0445 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0438 \u0443\u0431\u0435\u0434\u0438\u043b\u0438\u0441\u044c, \u0447\u0442\u043e \u043c\u043e\u0436\u0435\u043c \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u0442\u0430\u043a\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0438. \u041d\u043e \u043a\u043e\u043d\u0435\u0447\u043d\u043e \u0432\u0430\u043c \u043d\u0438\u043a\u0442\u043e \u043d\u0435 \u0437\u0430\u043f\u0440\u0435\u0449\u0430\u0435\u0442 \u0437\u0430\u043c\u0435\u043d\u0438\u0442\u044c <code>scipy.signal.fftconvolve<\/code> \u043d\u0430 \u043d\u0430\u0448\u0443 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e.<br \/>\u0412 \u0441\u0432\u0451\u0440\u0442\u043e\u0447\u043d\u044b\u0445 \u0441\u043b\u043e\u044f\u0445 \u0442\u0430\u043a\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 bias, \u043a\u0430\u043a \u0432 \u043b\u0438\u043d\u0435\u0439\u043d\u044b\u0445 \u0441\u043b\u043e\u044f\u0445, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u0441\u0442\u0435\u043f\u0435\u043d\u044c \u0441\u0432\u043e\u0431\u043e\u0434\u044b \u043f\u0440\u0438 \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u0438. \u041c\u0435\u0442\u043e\u0434 <code>.backward()<\/code> \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0447\u0443\u0442\u044c \u043f\u043e\u0437\u0436\u0435<br \/>\u0420\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u0442\u0430\u043a\u0436\u0435 \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 <a href=\"https:\/\/www.youtube.com\/playlist?list=PL1sQgSTcAaT7MbcLWacjsqoOQvqzMdUWg\" rel=\"noopener noreferrer nofollow\">\u0434\u0430\u043d\u043d\u044b\u043c \u043f\u043b\u0435\u0439\u043b\u0438\u0441\u0442\u043e\u043c<\/a>, \u0441 <a href=\"https:\/\/www.youtube.com\/playlist?list=PLuhqtP7jdD8CD6rOWy20INGM44kULvrHu\" rel=\"noopener noreferrer nofollow\">\u0434\u0430\u043d\u043d\u044b\u043c \u043f\u043b\u0435\u0439\u043b\u0438\u0441\u0442\u043e\u043c<\/a> \u0438 \u0441 <a href=\"https:\/\/www.youtube.com\/watch?v=m8pOnJxOcqY\" rel=\"noopener noreferrer nofollow\">\u0432\u0438\u0434\u0435\u043e<\/a><\/p>\n<p>\u0418\u0434\u0451\u043c \u0434\u0430\u043b\u044c\u0448\u0435. \u041f\u0440\u0435\u0434\u043b\u0430\u0433\u0430\u044e \u043d\u0435 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u043e\u0434\u043d\u043e\u0439 \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439 \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438!<\/p>\n<p><strong>Sigmoid<\/strong><\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/cfe\/aac\/a63\/cfeaaca636b83ff2d051255bb68f20f8.png\" width=\"992\" height=\"993\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/cfe\/aac\/a63\/cfeaaca636b83ff2d051255bb68f20f8.png\"\/><\/figure>\n<pre><code class=\"python\">class Sigmoid:      def __init__(self):         self.input = None         Parameter([self, []])      @staticmethod     def sigmoid_(x):         return 1 \/ (1 + np.exp(-x))      def __call__(self, x):         self.input = x         return self.sigmoid_(x)      def backward(self, input_matrix):         return (1 - self.sigmoid_(self.input)) * self.sigmoid_(self.input) * input_matrix<\/code><\/pre>\n<p><strong>Leaky Relu<\/strong><\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/c83\/e5f\/dc1\/c83e5fdc1069ee980c616367aa29363e.png\" width=\"392\" height=\"248\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/c83\/e5f\/dc1\/c83e5fdc1069ee980c616367aa29363e.png\"\/><\/figure>\n<pre><code class=\"python\">class Leaky_relu:      def __init__(self, a = 0.2):         self.a = a         Parameter([self, []])      def __call__(self, x):         self.input = np.array(x, copy=True)         return x * ((1 + np.sign(x)) \/ 2 + self.a * (1 + np.sign(-x)) \/ 2)      def derivative(self, input_matrix):         return ((1 + np.sign(self.input)) \/ 2 + self.a * (1 + np.sign(-self.input)) \/ 2) * input_matrix          <\/code><\/pre>\n<p><strong>Tanh<\/strong><\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/2f7\/dcf\/5b1\/2f7dcf5b1bc65c6cd0f82c1b4176837b.png\" width=\"646\" height=\"430\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/2f7\/dcf\/5b1\/2f7dcf5b1bc65c6cd0f82c1b4176837b.png\"\/><\/figure>\n<pre><code class=\"python\">class Tanh:      def __init__(self):         self.input = None         Parameter([self, []])      @staticmethod     def tanh_(x):         return (np.exp(2 * x) - 1) \/ (np.exp(2 * x) + 1)      def __call__(self, x):         self.input = np.array(x, copy=True)         return self.tanh_(x)      def derivative(self, input_matrix):         return 1 - self.tanh_(self.input) * input_matrix <\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0442\u0435\u043f\u0435\u0440\u044c <a href=\"https:\/\/towardsdatascience.com\/l1-and-l2-regularization-methods-ce25e7fc831c\" rel=\"noopener noreferrer nofollow\">\u0440\u0435\u0433\u0443\u043b\u044f\u0440\u0438\u0437\u0430\u0446\u0438\u044e<\/a>, \u043e\u043d\u0430 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0435\u043b\u0438\u0447\u0438\u043d\u0443 \u0432\u0435\u0441\u043e\u0432 \u043c\u043e\u0434\u0435\u043b\u0438.<\/p>\n<pre><code class=\"python\">class CrossEntropyLoss:      def __init__(self, l1_reg = 0, l2_reg = 0):         self.l1_reg = l1_reg         self.l2_reg = l2_reg     def backward(self):         self.backward_list = []         loss = self.predicted - self.true         for index, layer in enumerate(Parameter.layers[::-1]):             if type(layer).__name__ == 'Linear':             base = (layer.x.T @ loss) \/ loss.shape[0]             l1_term = self.l1_reg * np.sign(Parameter.calling[layer][0])             l2_term = self.l2_reg * Parameter.calling[layer][0]                 changes_w = base + l1_term + l2_term ... ... <\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u043e\u0432\u044b\u0435 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u044b \u0433\u0440\u0430\u0434\u0438\u0435\u043d\u0442\u043d\u043e\u0433\u043e \u0441\u043f\u0443\u0441\u043a\u0430<\/p>\n<h2>Nesterov Accelerated Gradient<\/h2>\n<p> \u041e\u0447\u0435\u043d\u044c \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430\u043c\u0438 <a href=\"https:\/\/youtu.be\/lRWiWwS11Yo?list=PLA0M1Bcd0w8zxDIDOTQHsX68MCDOAJDtj&amp;t=217\" rel=\"noopener noreferrer nofollow\">\u043f\u0435\u0440\u0432\u043e\u0435<\/a> \u0438 <a href=\"https:\/\/youtu.be\/7wYxGcoZO_c?list=PLZu_eiiJs4b4hNw-YPz6I3sXY63K31ZYc&amp;t=1114\" rel=\"noopener noreferrer nofollow\">\u0432\u0442\u043e\u0440\u043e\u0435<\/a><\/p>\n<pre><code class=\"python\">class NAG:     def __init__(self, model, learning_rate, momentum):         self.model = model         self.lr = learning_rate         self.momentum = momentum         self.last_grad_w = None # \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0437\u0434\u0435\u0441\u044c \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0439 \u0433\u0440\u0430\u0434\u0438\u0435\u043d\u0442          self.last_grad_b = None # \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0437\u0434\u0435\u0441\u044c \u043f\u0440\u0435\u0434\u044b\u0434\u0443\u0449\u0438\u0439 \u0433\u0440\u0430\u0434\u0438\u0435\u043d\u0442      def step(self):     if self.last_grad_w == None:             self.last_grad_w = [0] * len(self.model._constructor_Parameter.layers) # \u0445\u0440\u0430\u043d\u0438\u043c \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0441\u043b\u043e\u044f \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e              self.last_grad_b = [0] * len(self.model._constructor_Parameter.layers)                  for index, layer in enumerate(self.model._constructor_Parameter.layers[::-1]):             if type(layer).__name__ in ('Linear', 'Conv2d'):                 weight, bias = self.model._constructor_Parameter.calling[layer]                 weight_gradient, bias_gradient = layer.backward_list[0], layer.backward_list[1]                 # \u043c\u043e\u0436\u043d\u043e \u0441\u043a\u0430\u0437\u0430\u0442\u044c, \u0447\u0442\u043e \u044d\u0442\u043e \u0441\u043a\u0430\u043b\u044c\u0437\u044f\u0449\u0435\u0435 \u0441\u0440\u0435\u0434\u043d\u0435\u0435 \u0434\u043b\u044f \u0433\u0440\u0430\u0434\u0438\u0435\u043d\u0442\u043e\u0432                 self.last_grad_w[index] = - self.lr * weight_gradient + self.momentum * self.last_grad_w[index]                 self.last_grad_b[index] = - self.lr * bias_gradient + self.momentum * self.last_grad_b[index]                 # \u043e\u0431\u0432\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0432\u0435\u0441\u0430                 new_weight = weight + self.last_grad_w[index]                 new_bias = bias + self.last_grad_\u0438[index]                 # \u043c\u0435\u043d\u044f\u0435\u043c \u043d\u0430 \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u0432\u0435\u0441\u0430                 self.model._constructor_Parameter.calling[layer] = [new_weight, new_bias] <\/code><\/pre>\n<h2>RMSProp<\/h2>\n<p>\u041e\u0447\u0435\u043d\u044c \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430\u043c\u0438 <a href=\"https:\/\/youtu.be\/lRWiWwS11Yo?list=PLA0M1Bcd0w8zxDIDOTQHsX68MCDOAJDtj&amp;t=330\" rel=\"noopener noreferrer nofollow\">\u043f\u0435\u0440\u0432\u043e\u0435<\/a> \u0438 <a href=\"https:\/\/youtu.be\/7wYxGcoZO_c?list=PLZu_eiiJs4b4hNw-YPz6I3sXY63K31ZYc&amp;t=1950\" rel=\"noopener noreferrer nofollow\">\u0432\u0442\u043e\u0440\u043e\u0435<\/a><\/p>\n<pre><code class=\"python\">class RMSProp:     def __init__(self, model, learning_rate, ro):          self.model = model         self.lr = learning_rate         if ro &amp;lt; 0 or ro &amp;gt; 1:             raise Exception(\"Incorrect ro value\")         self.ro = ro         self.grad_velocity_w = None         self.grad_velocity_b = None      def step(self):          if self.grad_velocity_w== None:             self.grad_velocity_w = [0] * len(self.model._constructor_Parameter.layers)             self.grad_velocity_b = [0] * len(self.model._constructor_Parameter.layers)         for index, layer in enumerate(self.model._constructor_Parameter.layers[::-1]):             if type(layer).__name__ in ('Linear', 'Conv2d'):                 weight, bias = self.model._constructor_Parameter.calling[layer]                 weight_gradient, bias_gradient = layer.backward_list[0], layer.backward_list[1]                                  self.grad_velocity_w[index] = self.ro * self.grad_velocity_w[index] + (1 - self.ro) * weight_gradient ** 2                 self.grad_velocity_b[index] = self.ro * self.grad_velocity_b[index] + (1 - self.ro) * bias_gradient ** 2                  new_weight = weight - self.lr * weight_gradient \/ np.sqrt(self.grad_velocity_w[index] + 1e-5)                 new_bias = bias - self.lr * bias_gradient \/ np.sqrt(self.grad_velocity_b[index] + 1e-5)                                  self.model._constructor_Parameter.calling[layer] = [new_weight, new_bias] <\/code><\/pre>\n<h2>Adam<\/h2>\n<p> \u0421\u043d\u043e\u0432\u0430 \u043e\u0447\u0435\u043d\u044c \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430\u043c\u0438 <a href=\"https:\/\/youtu.be\/lRWiWwS11Yo?list=PLA0M1Bcd0w8zxDIDOTQHsX68MCDOAJDtj&amp;t=551\" rel=\"noopener noreferrer nofollow\">\u043f\u0435\u0440\u0432\u043e\u0435<\/a> \u0438 <a href=\"https:\/\/youtu.be\/7wYxGcoZO_c?list=PLZu_eiiJs4b4hNw-YPz6I3sXY63K31ZYc&amp;t=2232\" rel=\"noopener noreferrer nofollow\">\u0432\u0442\u043e\u0440\u043e\u0435<\/a><br \/> \u0413\u0440\u0443\u0431\u043e \u0433\u043e\u0432\u043e\u0440\u044f <code>adam<\/code> \u044d\u0442\u043e \u0441\u0443\u043c\u043c\u0430 <code>rmsprop<\/code> \u0438 <code>nag<\/code>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043f\u0440\u043e\u0441\u0442\u043e &#171;\u0441\u043b\u043e\u0436\u0438\u043c&#187; 2 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438<\/p>\n<pre><code class=\"python\">class Adam:     def __init__(self, model, learning_rate, momentum, ro):         self.model = model         self.lr = learning_rate         self.momentum = momentum         self.last_grad_w = None         self.last_grad_b = None          if ro &amp;lt; 0 or ro &amp;gt; 1:             raise Exception(\"Incorrect ro value\")          self.ro = ro         self.grad_velocity_w = None         self.grad_velocity_b = None       def step(self):          if self.last_grad_w == None:             self.last_grad_w = [0] * len(self.model._constructor_Parameter.layers)             self.last_grad_b = [0] * len(self.model._constructor_Parameter.layers)             self.grad_velocity_w = [0] * len(self.model._constructor_Parameter.layers)             self.grad_velocity_b = [0] * len(self.model._constructor_Parameter.layers)          for index, layer in enumerate(self.model._constructor_Parameter.layers[::-1]):             if type(layer).__name__ in ('Linear', 'Conv2d'):                 weight, bias = self.model._constructor_Parameter.calling[layer]                 weight_gradient, bias_gradient = layer.backward_list[0], layer.backward_list[1]                                  self.grad_velocity_w[index] = self.ro * self.grad_velocity_w[index] + (1 - self.ro) * weight_gradient ** 2                 self.grad_velocity_b[index] = self.ro * self.grad_velocity_b[index] + (1 - self.ro) * bias_gradient ** 2                                  self.last_grad_w[index] = - self.lr * weight_gradient + self.momentum * self.last_grad_w[index]                 self.last_grad_b[index] = - self.lr * bias_gradient + self.momentum * self.last_grad_b[index]                  new_weight = weight + self.last_grad_w[index] \/ np.sqrt(self.grad_velocity_w[index] + 1e-5)                 new_bias = bias + self.last_grad_b[index] \/ np.sqrt(self.grad_velocity_b[index] + 1e-5)                                  self.model._constructor_Parameter.calling[layer] = [new_weight, new_bias] <\/code><\/pre>\n<h2>\u041e\u0431\u0443\u0447\u0435\u043d\u0438\u0435<\/h2>\n<p>\u0418\u0442\u0430\u043a, \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u0432\u0441\u0435 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0435. \u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 \u0432\u0435\u0441\u043e\u0432 \u0434\u043b\u044f <code>Conv2d<\/code>. \u0421\u0435\u0439\u0447\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u0436\u043d\u043e\u0432\u0430\u0442\u043e, \u043d\u043e \u0443 \u043d\u0430\u0441 \u0432\u0441\u0451 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f!<\/p>\n<p>\u042f <strong>\u043e\u0447\u0435\u043d\u044c<\/strong> \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 \u0434\u0430\u043d\u043d\u044b\u043c\u0438 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u0430\u043c\u0438, \u0432 \u043d\u0438\u0445 \u0440\u0435\u0431\u044f\u0442\u0430 \u043d\u0430 \u043f\u0430\u043b\u044c\u0446\u0430\u0445 \u0440\u0430\u0441\u0441\u043a\u0430\u0437\u044b\u0432\u0430\u044e\u0442 \u043f\u0440\u043e \u0440\u0430\u0441\u0447\u0435\u0442 \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0433\u043e \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0447\u0435\u0440\u0435\u0437 \u0441\u0432\u0451\u0440\u0442\u043e\u0447\u043d\u044b\u0439 \u0441\u043b\u043e\u0439. \u042f \u0431\u0443\u0434\u0443 \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0438\u0445 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430\u043c\u0438<\/p>\n<ul>\n<li>\n<p><a href=\"https:\/\/youtu.be\/z9hJzduHToc?si=L6a1ACuL8f1_V7Wy\" rel=\"noopener noreferrer nofollow\">\u043f\u0435\u0440\u0432\u043e\u0435<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/www.youtube.com\/watch?v=Pn7RK7tofPg&amp;list=PLZu_eiiJs4b4hNw-YPz6I3sXY63K31ZYc&amp;index=72\" rel=\"noopener noreferrer nofollow\">\u0432\u0442\u043e\u0440\u043e\u0435<\/a><\/p>\n<\/li>\n<li>\n<p><a href=\"https:\/\/www.youtube.com\/watch?v=vbUozbkMhI0\" rel=\"noopener noreferrer nofollow\">\u0442\u0440\u0435\u0442\u044c\u0435<\/a><\/p>\n<\/li>\n<\/ul>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0441\u0442\u044c \u044d\u0442\u0438\u0445 \u0444\u043e\u0440\u043c\u0443\u043b \u0432 \u043e\u0431\u0449\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e <code>scipy.signal.fftconvolve<\/code><\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/bde\/92a\/03b\/bde92a03b118b2fb2f79af5cfe5c29e2.png\" width=\"811\" height=\"748\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/bde\/92a\/03b\/bde92a03b118b2fb2f79af5cfe5c29e2.png\"\/><\/figure>\n<pre><code class=\"python\">image = np.random.randn(10, 1, 3, 7, 7) # \u041d\u0430\u0448\u0430 \u0432\u0445\u043e\u0434\u043d\u0430\u044f \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430 kernel = np.random.randn(1, 5, 3, 3, 3) # \u041d\u0430\u0448\u0438 \u0444\u0438\u043b\u044c\u0442\u0440\u044b bias = np.random.randn(1, 5, 1, 1) # \u043d\u0430\u0448 bias  result = scipy.signal.fftconvolve(image, kernel, mode='valid') #(10, 5, 1, 5, 5) # \u041f\u0440\u0435\u0434\u043f\u043e\u043b\u043e\u0436\u0438\u043c, \u0447\u0442\u043e \u044d\u0442\u043e \u043d\u0430\u0448 dl\/dx - \u043f\u043e \u0440\u0430\u0437\u043c\u0435\u0440\u043d\u043e\u0441\u0442\u0438 \u043e\u043d \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0435\u0442 \u043a\u0430\u043a \u0440\u0430\u0437 <\/code><\/pre>\n<p>\u0421\u0447\u0438\u0442\u0430\u0435\u043c \u0433\u0440\u0430\u0434\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432 \u043f\u043e \u0444\u043e\u0440\u043c\u0443\u043b\u0435 \u0441 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438<\/p>\n<pre><code class=\"python\">scipy.signal.fftconvolve(image, result, mode='valid').shape &gt;&gt;&gt; (1, 5, 3, 3, 3) <\/code><\/pre>\n<p>\u0412\u0438\u0434\u0438\u043c, \u0447\u0442\u043e \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0435\u0442 \u0441 \u0440\u0430\u0437\u043c\u0435\u0440\u043d\u043e\u0441\u0442\u044c\u044e \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u0432. \u0418\u0434\u0451\u043c \u0434\u0430\u043b\u044c\u0448\u0435<br \/> \u0421\u0447\u0438\u0442\u0430\u0435\u043c \u0433\u0440\u0430\u0434\u0438\u0435\u043d\u0442 \u0434\u043b\u044f bias \u043f\u043e \u0444\u043e\u0440\u043c\u0443\u043b\u0435 \u0441 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438<\/p>\n<pre><code class=\"python\">result.sum(axis=(0, 2, 3, 4)) &gt;&gt;&gt; (5,) <\/code><\/pre>\n<p>\u0423 \u043d\u0430\u0441 \u0442\u0430\u043a\u0436\u0435 \u0432\u0441\u0451 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0435\u0442 \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f broadcasting<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/f20\/a88\/dad\/f20a88dadd6baf59cdcbbe206f4d392c.png\" width=\"432\" height=\"324\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f20\/a88\/dad\/f20a88dadd6baf59cdcbbe206f4d392c.png\"\/><\/figure>\n<p>\u0421\u0447\u0438\u0442\u0430\u0435\u043c \u0433\u0440\u0430\u0434\u0438\u0435\u043d\u0442 \u0434\u043b\u044f \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u0433\u043e \u043f\u0440\u043e\u0445\u043e\u0436\u0434\u0435\u043d\u0438\u044f \u043f\u043e \u0444\u043e\u0440\u043c\u0443\u043b\u0435 \u0441 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438<\/p>\n<pre><code class=\"python\">rot = np.transpose(kernel, (0, 1, 2, 4, 3)) # \u043f\u043e\u043c\u0435\u043d\u044f\u043b\u0438 \u043c\u0435\u0441\u0442\u0430\u043c\u0438 2 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0435 \u043e\u0441\u0438 pad = np.pad(result, ((0, 0), (0, 0), (0, 0), (2, 2), (2, 2)), 'constant', constant_values=(0)) # \u0417\u0430\u043f\u043e\u043b\u043d\u0438\u043b\u0438 2 \u043f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0435 \u0440\u0430\u0437\u043c\u0435\u0440\u043d\u043e\u0441\u0442\u0438 0 \u0441\u043b\u0435\u0432\u0430 \u0438 \u0441\u043f\u0440\u0430\u0432\u0430<\/code><\/pre>\n<p>\u041f\u0440\u0438\u043c\u0435\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f <code>np.pad<\/code><\/p>\n<pre><code class=\"python\">tmp = np.random.randn(2, 2) np.pad(tmp, ((1, 1), (1, 1)), 'constant', constant_values=(0)) &gt;&gt;&gt;array([[ 0.        ,  0.        ,  0.        ,  0.        ],        [ 0.        ,  1.93470843, -1.40590807,  0.        ],        [ 0.        , -0.61160614,  0.15255254,  0.        ],        [ 0.        ,  0.        ,  0.        ,  0.        ]]) <\/code><\/pre>\n<pre><code class=\"python\">scipy.signal.fftconvolve(pad, rot, mode='valid').shape &gt;&gt;&gt; (10, 1, 3, 7, 7)<\/code><\/pre>\n<p>\u0418 \u0443 \u043d\u0430\u0441 \u0432\u0441\u0451 \u043e\u043f\u044f\u0442\u044c \u0441\u043e\u0448\u043b\u043e\u0441\u044c \u0441 \u0440\u0430\u0437\u043c\u0435\u0440\u043d\u043e\u0441\u0442\u044c\u044e <code>image<\/code><br \/>\u0417\u043d\u0430\u0447\u0438\u0442 \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u044d\u0442\u0438 \u0444\u043e\u0440\u043c\u0443\u043b\u044b \u0434\u043b\u044f \u0440\u0430\u0441\u0447\u0435\u0442\u043e\u0432 \u0433\u0440\u0430\u0434\u0438\u0435\u043d\u0442\u043e\u0432. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u0430\u043a\u043e\u043d\u0435\u0446-\u0442\u043e \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u0432 \u043a\u043b\u0430\u0441\u0441 <code>CrossEntropyLoss<\/code><\/p>\n<pre><code class=\"python\">class CrossEntropyLoss:      def backward(self):         for index, layer in enumerate(Parameter.layers[::-1]):             elif type(layer).__name__ == 'Conv2d':                 expanded_loss = np.expand_dims(loss, axis=2)                 changes_w = scipy.signal.fftconvolve(layer.x, expanded_loss, mode='valid')                 changes_b = loss.sum(axis=(0, 2, 3)) * self.bias_flag                 layer.backward_list = [changes_w, changes_b]                              rotated_filters = np.transpose(layer.filter_array, (0, 1, 2, 4, 3))                 pad = layer.kernel_size[-1]                 padded_loss = np.pad(expanded_loss, ((0, 0), (0, 0), (0, 0),  (pad - 1, pad - 1), (pad - 1, pad - 1)), 'constant', constant_values=(0))                  loss = scipy.signal.fftconvolve(padded_loss, rotated_filters, mode='valid').squeeze(axis=1)                  <\/code><\/pre>\n<p>\u0413\u043e\u0442\u043e\u0432\u043e! \u041e\u043a\u0430\u0437\u0430\u043b\u043e\u0441\u044c \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435\u0441\u043b\u043e\u0436\u043d\u043e, \u0434\u0430?)<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u043f\u043e\u043c\u0435\u043d\u044f\u0435\u043c \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 <code>Flatten<\/code>. \u041e\u043d \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f \u043f\u043e\u0441\u043b\u0435 \u0432\u0441\u0435\u0445 \u0441\u0432\u0435\u0440\u0442\u043e\u0447\u043d\u044b\u0445 \u0441\u043b\u043e\u0451\u0432 \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0435\u0432\u0440\u0430\u0442\u0438\u0442\u044c \u0432\u0435\u043a\u0442\u043e\u0440 \u0438\u0437 <code>(batch, channels, height, width)<\/code> \u0432 <code>(batch, channels * height * width)<\/code> \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u044d\u0442\u043e\u0442 \u0432\u0435\u043a\u0442\u043e\u0440 \u0432 \u043b\u0438\u043d\u0435\u0439\u043d\u044b\u0439 \u0441\u043b\u043e\u0439. \u0422\u0430\u043a\u0436\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0434\u043b\u044f \u043d\u0435\u0433\u043e \u043c\u0435\u0442\u043e\u0434 <code>.backward<\/code>, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043e\u043d \u0443\u0436\u0435 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043e\u0447\u043d\u044b\u043c \u0441\u043b\u043e\u0435\u043c.<\/p>\n<pre><code class=\"python\">class Flatten:     def __init__(self):          Parameter([self, []])     def __call__(self, x): self.init_shape = x.shape         return x.reshape(self.init_shape[0], -1)     def backward(self, input_matrix):     return input_matrix.reshape(self.init_shape)  class CrossEntropyLoss:     def backward(self):     for index, layer in enumerate(Parameter.layers[::-1]):         ... elif type(layer).__name__ == 'Flatten':                 loss = layer.backward(loss) <\/code><\/pre>\n<p>\u0412\u0441\u0451 \u0433\u043e\u0442\u043e\u0432\u043e! \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u043e\u0431\u0438\u0440\u0430\u0442\u044c \u0438 \u043e\u0431\u0443\u0447\u0430\u0442\u044c \u043d\u0430\u0448\u0443 \u0441\u0432\u0451\u0440\u0442\u043e\u0447\u043d\u0443\u044e \u043d\u0435\u0439\u0440\u043e\u043d\u043a\u0443!<\/p>\n<pre><code class=\"python\">class SimpleNet(Module):     def __init__(self):         super().__init__()         self.conv1 = Conv2d(input_channels = 1, output_channels = 4, kernel_size=5) #28 -&gt; 24         self.conv2 = Conv2d(input_channels = 4, output_channels = 8, kernel_size=5) #24 -&gt; 20         self.conv3 = Conv2d(input_channels = 8, output_channels = 16, kernel_size=5) #20 -&gt; 16         self.flatten = Flatten()         self.linear1 = Linear(input_channels=16 * 16 * 16, output_channels=200, bias=True)         self.linear2 = Linear(input_channels=200, output_channels=50, bias=True)         self.linear3 = Linear(input_channels=50, output_channels=10, bias=True)         self.relu = ReLU()      def forward(self, x):         x = self.conv1(x)         x = self.relu(x)         x = self.conv2(x)         x = self.relu(x)         x = self.conv3(x)         x = self.relu(x)         x = self.flatten(x)         x = self.linear1(x)         x = self.relu(x)         x = self.linear2(x)         x = self.relu(x)         x = self.linear3(x)         return x          loss_fn = CrossEntropyLoss(l1_reg=0.1, l2_reg=0.1) model = SimpleNet() optim = Adam(model.parameters(), learning_rate = 0.001, momentum=0.9, ro=0.9)  for i in range(5):     for index, batch in enumerate(data_loader):         input_x, target = batch         input_x = input_x \/ 255         input_x = np.expand_dims(input_x, axis=1) # (64, 28, 28) -&gt; (64, 1, 28, 28)         output = model(input_x)         loss = loss_fn(output, target)         loss.backward()         optim.step()          if index % 1 == 0:             print(loss.loss.mean(),\"index:\", index)              print(loss.loss.mean(), \"epoch:\", i)  &gt;&gt;&gt;7.968706513917543 index: 0 7.551666321134583 index: 1 7.278343803030227 index: 2 6.431693753643258 index: 3 5.637298937703794 index: 4 5.633115375065631 index: 5 5.00220357224557 index: 6 5.925713000464821 index: 7 5.045218328819374 index: 8 5.103600182770298 index: 9 4.13759075103372 index: 10 3.576208929225664 index: 11 3.320147584705806 index: 12 2.995831081105269 index: 13 2.8976790588242745 index: 14 2.8176135148843797 index: 15 2.7363129802870807 index: 16 2.297198679475752 index: 17 2.310984252722919 index: 18 2.2726362928678476 index: 19 2.11617617635163 index: 20 2.23533730800758 index: 21 2.064323468827378 index: 22 1.9638605959573385 index: 23 2.1304361443736948 index: 24 2.0881922952210124 index: 25 1.9733052417437202 index: 26 2.099729829345195 index: 27 2.0752790945980193 index: 28 <\/code><\/pre>\n<p>\u041e\u0442\u043b\u0438\u0447\u043d\u043e, \u043c\u043e\u0434\u0435\u043b\u044c \u043a\u043e\u0435-\u043a\u0430\u043a \u043e\u0431\u0443\u0447\u0430\u0435\u0442\u0441\u044f, \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u043e\u0442 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 loss-\u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0432 \u043a\u0430\u043a\u043e\u0439-\u0442\u043e \u043c\u043e\u043c\u0435\u043d\u0442 \u043f\u0435\u0440\u0435\u0441\u0442\u0430\u0435\u0442 \u0438\u0437\u043c\u0435\u043d\u044f\u0442\u0441\u044f. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e \u043f\u0440\u0438\u0447\u0438\u043d\u043e\u0439 \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u0436\u0435\u0442 \u0431\u044b\u0442\u044c \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0441\u043b\u043e\u0436\u043d\u0430\u044f \u0437\u0430\u0434\u0430\u0447\u0430 \u0438\u043b\u0438 \u0441\u043b\u0438\u0448\u043a\u043e\u043c \u0441\u043b\u043e\u0436\u043d\u0430\u044f \u043c\u043e\u0434\u0435\u043b\u044c (\u0432 \u043e\u0434\u043d\u043e\u043c \u0442\u043e\u043b\u044c\u043a\u043e <code>linear1 - 16*16*16*200 = 800_000 \u0432\u0435\u0441\u043e\u0432<\/code>)<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u043c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0437\u0430\u0434\u0430\u0447\u0443 \u043d\u0430\u0448\u0435\u0439 \u043c\u043e\u0434\u0435\u043b\u0438, \u0432\u0432\u0435\u0434\u044f \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u0441\u043b\u043e\u0439 &#8212; <code>MaxPool<\/code><\/p>\n<h2>MaxPool<\/h2>\n<p>Pooling \u2014 \u044d\u0442\u043e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f \u0443\u043c\u0435\u043d\u044c\u0448\u0435\u043d\u0438\u044f \u043a\u0430\u0440\u0442 \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438. \u0414\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u043e\u043d\u0430 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u043e:<\/p>\n<ul>\n<li>\n<p>\u0411\u0435\u0440\u0435\u043c \u043a\u0430\u0440\u0442\u0443 \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438, \u0434\u0435\u043b\u0438\u043c \u0435\u0435 \u043d\u0430 \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u044b \u0440\u0430\u0437\u043c\u0435\u0440\u0430 \u0434\u0432\u0430 \u043d\u0430 \u0434\u0432\u0430;<\/p>\n<\/li>\n<li>\n<p>\u0418\u0437 \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u0430 \u0434\u0432\u0430 \u043d\u0430 \u0434\u0432\u0430 \u0431\u0435\u0440\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u043d\u043e \u0447\u0438\u0441\u043b\u043e: \u043c\u0430\u043a\u0441\u0438\u043c\u0430\u043b\u044c\u043d\u043e\u0435 \u0447\u0438\u0441\u043b\u043e \u0432 \u044d\u0442\u043e\u043c \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u0435;<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u044d\u0442\u0438 \u0447\u0438\u0441\u043b\u0430 \u0432\u043c\u0435\u0441\u0442\u043e \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u0430 \u0434\u0432\u0430 \u043d\u0430 \u0434\u0432\u0430. \u0422\u0430\u043a \u043c\u044b \u0438\u0437 \u043a\u0430\u0440\u0442\u044b \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438 \u0440\u0430\u0437\u043c\u0435\u0440\u0430 (n x n) \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043a\u0430\u0440\u0442\u0443 \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438 \u0440\u0430\u0437\u043c\u0435\u0440\u0430 (n\/2 x n\/2)<\/p>\n<\/li>\n<\/ul>\n<p>\u0422\u0430\u043a\u0430\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f MaxPooling \u0441 \u044f\u0434\u0440\u043e\u043c \u0440\u0430\u0437\u043c\u0435\u0440\u0430 2. \u042f\u0434\u0440\u043e \u0440\u0430\u0437\u043c\u0435\u0440\u0430 2, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043c\u044b \u0434\u0435\u043b\u0438\u043b\u0438 \u043a\u0430\u0440\u0442\u0443 \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438 \u043d\u0430 \u043a\u0432\u0430\u0434\u0440\u0430\u0442\u044b \u0440\u0430\u0437\u043c\u0435\u0440\u0430 2\u04452.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/740\/0ad\/56c\/7400ad56cd919ebfe71d5b23baa2b1aa.png\" width=\"610\" height=\"337\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/740\/0ad\/56c\/7400ad56cd919ebfe71d5b23baa2b1aa.png\"\/><\/figure>\n<pre><code class=\"python\">class MaxPool:      def __init__(self, kernel_size: tuple):         self.kernel_size = kernel_size         Parameter([self, []])      def __call__(self, x):         array = x.copy()         result_full = np.zeros((array.shape[0], array.shape[1], int(array.shape[2] \/ self.kernel_size[0]), int(array.shape[3] \/ self.kernel_size[1])))         for k in range(array.shape[0]):             for m in range(array.shape[1]):                 result = []                 self.i = 0                 while self.i &lt; array[k][m].shape[0] - self.kernel_size[0] + 1:                     self.j = 0                     while self.j &lt; array[k][m].shape[1] - self.kernel_size[1] + 1:                         result.append(np.max(array[k][m][self.i:self.i + self.kernel_size[0], self.j:self.j + self.kernel_size[1]]))                         array[k][m][self.i:self.i + self.kernel_size[0], self.j:self.j + self.kernel_size[1]] = (array[k][m][self.i:self.i + self.kernel_size[0], self.j: self.j + self.kernel_size[1]]) * [array[k][m][self.i:self.i + self.kernel_size[0],                                                                                                       self.j:self.j +self.kernel_size[1]] == np.max(array[k][m][self.i:self.i +self.kernel_size[0], self.j:self.j +self.kernel_size[1]])]                          self.j += self.kernel_size[1]                     self.i += self.kernel_size[0]                  result_full[k][m] = np.array(result).reshape(int(array[k][m].shape[0] \/ self.kernel_size[0]), int(array[k][m].shape[1] \/ self.kernel_size[1]))          self.array = array         return result_full<\/code><\/pre>\n<p>\u041a\u0430\u043a \u0432\u0438\u0434\u0438\u043c, \u043b\u043e\u0433\u0438\u043a\u0430 \u043f\u0440\u043e\u0441\u0442\u0430\u044f, \u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043d\u0435 \u043e\u0447\u0435\u043d\u044c. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0437\u0430\u043c\u0435\u043d\u0438\u043c \u043d\u0430 \u0431\u043e\u043b\u0435\u0435 \u043a\u043e\u0440\u043e\u0442\u043a\u0443\u044e \u0444\u0443\u043d\u043a\u0446\u0438\u044e <code>skimage.measure.block_reduce<\/code> \u0438 \u0441\u0440\u0430\u0432\u043d\u0438\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u044b<\/p>\n<pre><code class=\"python\">import skimage  class MaxPool2d:     def __init__(self, kernel_size: tuple):         self.kernel_size = kernel_size         Parameter([self, []])      def __call__(self, x):     self.x = np.array(x, copy=True)     return skimage.measure.block_reduce(a, (1, 1, *self.kernel_size), np.max)  class MinPool2d:     def __init__(self, kernel_size: tuple):         self.kernel_size = kernel_size         Parameter([self, []])      def __call__(self, x):     self.x = np.array(x, copy=True)     return skimage.measure.block_reduce(a, (1, 1, *self.kernel_size), np.min) <\/code><\/pre>\n<pre><code class=\"python\">a = np.random.randn(10, 20, 30, 30) maxpool1 = MaxPool1((2, 2)) # \u041d\u0430\u0448\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f maxpool2 = MaxPool2((2, 2)) # \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f Scimage np.allclose(maxpool1(a), maxpool2(a)) &gt;&gt;&gt;True a = np.random.randn(4, 19, 20, 20) maxpool1 = MaxPool1((5, 5)) # \u041d\u0430\u0448\u0430 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f maxpool2 = MaxPool2((5, 5)) # \u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f Scimage np.allclose(maxpool1(a), maxpool2(a)) &gt;&gt;&gt;True <\/code><\/pre>\n<p>\u0420\u0430\u0437 \u043e\u043d\u0438 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0435, \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043e\u0441\u0442\u0430\u0432\u0438\u043c \u0431\u043e\u043b\u0435\u0435 \u043a\u043e\u0440\u043e\u0442\u043a\u0443\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e. \u0423\u0433\u043b\u0443\u0431\u043b\u044f\u0442\u044c\u0441\u044f \u0432 \u0441\u0432\u043e\u044e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e \u044f \u043d\u0435 \u0441\u0442\u0430\u043d\u0443, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d\u0430 \u0434\u043e\u0432\u043e\u043b\u044c\u043d\u0430\u044f \u043f\u0440\u043e\u0441\u0442\u0430\u044f, \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0442\u0430\u043d\u043d\u0430\u044f.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0434\u043e\u0431\u0430\u0432\u0438\u043c <code>backprop<\/code> \u0434\u043b\u044f max-min pool<br \/> \u041b\u043e\u0433\u0438\u043a\u0430 \u043f\u0440\u043e\u0441\u0442\u0430\u044f, \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u043d\u0443\u043b\u0435\u0432\u0443\u044e \u043c\u0430\u0442\u0440\u0438\u0446\u0443, \u0438 \u043d\u0430 \u0442\u0435\u0445 \u043c\u0435\u0441\u0442\u0430\u0445, \u0433\u0434\u0435 \u0431\u044b\u043b\u043e \u043d\u0430\u0438\u0431\u043e\u043b\u044c\u0448\u0435\u0435 ( \u0438\u043b\u0438 \u043d\u0430\u0438\u043c\u0435\u043d\u044c\u0448\u0435 \u0432 \u0441\u043b\u0443\u0447\u0430\u0435 <code>MinPool<\/code>) \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432 \u043e\u043a\u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u043c <code>0<\/code> \u043d\u0430 <code>1<\/code>. \u0412\u0441\u0451 \u043f\u0440\u043e\u0441\u0442\u043e, \u043d\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0432\u0441\u0451 \u0442\u0430\u043a\u0436\u0435 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0443\u0442\u0430\u043d\u043d\u0430\u044f! \u042f \u0432\u0435\u0440\u044e, \u0447\u0442\u043e \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0431\u043e\u043b\u0435\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e, \u043d\u043e \u043f\u043e\u043a\u0430 \u043d\u0435 \u0445\u043e\u0447\u0443 \u043e\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u043d\u0430 \u044d\u0442\u043e\u043c!<\/p>\n<pre><code class=\"python\"> class CrossEntropyLoss:     def backward(self):     for index, layer in enumerate(Parameter.layers[::-1]):         ... elif type(layer).__name__ == 'MaxPool2d':                 new_shape = np.zeros(layer.x.shape)                 for k in range(layer.x.shape[0]):                     for m in range(layer.x.shape[1]):                         inx_ = 0                         inx__ = 0                         layer.i = 0                         while layer.i &amp;lt; layer.x[k][m].shape[0] - layer.kernel_size[0] + 1:                             layer.j = 0                             inx__ = 0                             while layer.j &amp;lt; layer.x[k][m].shape[1] - layer.kernel_size[1] + 1:                                 new_shape[k][m][layer.i:layer.i + layer.kernel_size[0], layer.j:layer.j + layer.kernel_size[1]] = loss[k][m][inx_][inx__]                                 inx__ += 1                                 layer.j += layer.kernel_size[1]                              inx_ += 1                             layer.i += layer.kernel_size[0]                  loss = np.squeeze([layer.x &amp;gt; 0] * new_shape, axis=0) # \u0414\u043b\u044f minpool \u0430\u043d\u0430\u043b\u043e\u0433\u0438\u0447\u043d\u043e, \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u0437\u043d\u0430\u043a <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0431\u0438\u0440\u0430\u0435\u043c<\/p>\n<pre><code class=\"python\">class SimpleConvNet(Module):     def __init__(self):         super().__init__()         self.conv1 = Conv2d(input_channels = 1, output_channels = 4, kernel_size=3) #28 -&gt; 26         self.maxpool1 = MaxPool2d(kernel_size=(2,2)) # 26 -&gt; 13         self.conv2 = Conv2d(input_channels = 4, output_channels = 8, kernel_size=4) #13 -&gt; 10         self.maxpool2 = MaxPool2d(kernel_size=(2,2)) # 10 -&gt; 5         self.flatten = Flatten()         self.linear1 = Linear(input_channels= 5 * 5 * 8, output_channels=50, bias=True)         self.linear2 = Linear(input_channels=50, output_channels=10, bias=True)         self.relu = ReLU()      def forward(self, x):         x = self.conv1(x)         x = self.relu(x)         x = self.maxpool1(x)         x = self.conv2(x)         x = self.relu(x)         x = self.maxpool2(x)         x = self.flatten(x)         x = self.linear1(x)         x = self.relu(x)         x = self.linear2(x)         return x  loss_fn = CrossEntropyLoss(l1_reg=0.001, l2_reg=0.001) model = SimpleConvNet() optim = Adam(model.parameters(), learning_rate = 5e-3, momentum=0.9, ro=0.9)  for i in range(5):     y_pred_list = []     y_true_list = []     for index, batch in enumerate(data_loader):         input_x, target = batch         input_x = input_x \/ 255         input_x = np.expand_dims(input_x, axis=1) # (64, 28, 28) -&amp;gt; (64, 1, 28, 28)         output = model(input_x)         loss = loss_fn(output, target)         loss.backward()         optim.step()          y_pred_list.extend(output)         y_true_list.extend(np.int_(np.arange(0, 10) == target))          if index % 20 == 0:             print(f\"loss: {loss.loss.mean():.2f}\",\"index:\", index, 'acc:', f\"{accuracy(np.array(y_true_list), np.array(y_pred_list)):.2f}\")              print(f\"loss: {loss.loss.mean():.2f}\", \"epoch:\", i, 'acc:', f\"{accuracy(np.array(y_true_list), np.array(y_pred_list)):.2f}\")  &gt;&gt;&gt;loss: 2.94 index: 0 acc: 0.08 loss: 2.02 index: 20 acc: 0.23 loss: 1.76 epoch: 0 acc: 0.31 loss: 1.67 index: 0 acc: 0.53 loss: 1.06 index: 20 acc: 0.60 loss: 1.05 index: 40 acc: 0.63 loss: 0.98 index: 60 acc: 0.65 loss: 1.05 epoch: 1 acc: 0.65 loss: 0.98 index: 0 acc: 0.72 loss: 0.92 index: 20 acc: 0.64 loss: 1.27 index: 40 acc: 0.62 loss: 1.04 index: 60 acc: 0.62 loss: 0.97 epoch: 2 acc: 0.63 loss: 0.73 index: 0 acc: 0.77 <\/code><\/pre>\n<p><strong>\u041e\u0442\u043b\u0438\u0447\u043d\u043e<\/strong>! \u0423 \u043d\u0430\u0441 \u0432\u0441\u0451 \u043e\u0431\u0443\u0447\u0430\u0435\u0442\u0441\u044f!<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u043c\u0435\u0442\u043e\u0434 \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u0438\u0437\u0430\u0446\u0438\u0438 <a href=\"https:\/\/neerc.ifmo.ru\/wiki\/index.php?title=Batch-normalization\" rel=\"noopener noreferrer nofollow\">BatchNorm<\/a>. \u0411\u0443\u0434\u0435\u043c \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0442\u044c \u0435\u0433\u043e \u043f\u043e\u0441\u043b\u0435 \u043b\u0438\u043d\u0435\u0439\u043d\u044b\u0445 \u0441\u043b\u043e\u0435\u0432. <a href=\"https:\/\/kevinzakka.github.io\/2016\/09\/14\/batch_normalization\/\" rel=\"noopener noreferrer nofollow\">\u0412\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u043d\u043e\u0439 \u0434\u043b\u044f BatchNorm<\/a>. \u0417\u0434\u0435\u0441\u044c \u043e\u043f\u044f\u0442\u044c \u0436\u0435 \u043f\u0440\u043e\u0441\u0442\u043e \u043c\u0435\u0445\u0430\u043d\u0438\u0447\u0435\u0441\u043a\u0430\u044f \u0440\u0430\u0431\u043e\u0442\u0430 \u0432 \u0432\u0438\u0434\u0435 \u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u044f \u0444\u043e\u0440\u043c\u0443\u043b\u0430\u043c!<\/p>\n<pre><code class=\"python\">class BatchNorm2d:      def __init__(self, size):         self.conv = False         Parameter([self, np.ones((size)), np.ones((size))])      def __call__(self, x): self.mean = np.mean(x, axis=(0)) self.std = np.std(x, axis=(0)) + 0.0001 self.x = (x - self.mean) \/ self.std return Parameter.calling[self][0] * self.x + Parameter.calling[self][1]   class CrossEntropyLoss:     def backward(self):     for index, layer in enumerate(Parameter.layers[::-1]):         ... layer.backward_list = [np.sum(loss * layer.x, axis = 0), np.sum(loss, axis = 0)] dl_dx = loss * Parameter.calling[layer][0] dl_dstd = np.sum(dl_dx * (layer.x * layer.std) * (-1\/2) \/ (layer.std ** 3), axis = 0) dl_dmean = np.sum(dl_dx * (- 1 \/ layer.std), axis = 0) + dl_dstd * (np.sum(-2 * layer.x * layer.std, axis = 0) \/ len(layer.x)) loss = dl_dx \/ layer.std + dl_dstd * 2 * (layer.x * layer.std) \/ len(layer.x) + dl_dmean \/ len(layer.x)  <\/code><\/pre>\n<p>\u041e\u0431\u0443\u0447\u0430\u0435\u043c<\/p>\n<pre><code class=\"python\">class SimpleConvNet(Module):     def __init__(self):         super().__init__()         self.conv1 = Conv2d(input_channels = 1, output_channels = 4, kernel_size=3) #28 -&gt; 26         self.maxpool1 = MaxPool2d(kernel_size=(2,2)) # 26 -&gt; 13         self.conv2 = Conv2d(input_channels = 4, output_channels = 8, kernel_size=4) #13 -&gt; 10         self.maxpool2 = MaxPool2d(kernel_size=(2,2)) # 10 -&gt; 5         self.flatten = Flatten()         self.linear1 = Linear(input_channels=5 * 5 * 8, output_channels=50, bias=True)         self.bn = BatchNorm2d(50)         self.linear2 = Linear(input_channels=50, output_channels=10, bias=True)         self.relu = ReLU()      def forward(self, x):         x = self.conv1(x)         x = self.relu(x)         x = self.maxpool1(x)         x = self.conv2(x)         x = self.relu(x)         x = self.maxpool2(x)         x = self.flatten(x)         x = self.linear1(x)         x = self.bn(x)         x = self.relu(x)         x = self.linear2(x)         return x  loss_fn = CrossEntropyLoss(l1_reg=0.005, l2_reg=0.005) model = SimpleConvNet() optim = Adam(model.parameters(), learning_rate = 1e-3, momentum=0.99, ro=0.99)  for i in range(5):     y_pred_list = []     y_true_list = []     for index, batch in enumerate(data_loader):         input_x, target = batch         input_x = input_x \/ 255         input_x = np.expand_dims(input_x, axis=1) # (64, 28, 28) -&amp;gt; (64, 1, 28, 28)         output = model(input_x)         loss = loss_fn(output, target)         loss.backward()         optim.step()          y_pred_list.extend(output)         y_true_list.extend(np.int_(np.arange(0, 10) == target))          if index % 20 == 0:             print(f\"loss: {loss.loss.mean():.2f}\",\"index:\", index, 'acc:', f\"{accuracy(np.array(y_true_list), np.array(y_pred_list)):.2f}\")              print(f\"loss: {loss.loss.mean():.2f}\", \"epoch:\", i, 'acc:', f\"{accuracy(np.array(y_true_list), np.array(y_pred_list)):.2f}\")  &gt;&gt;&gt;loss: 4.56 index: 0 acc: 0.12 loss: 0.66 index: 20 acc: 0.59 loss: 0.74 epoch: 0 acc: 0.65 loss: 0.59 index: 0 acc: 0.78 loss: 1.24 index: 20 acc: 0.74 loss: 0.55 index: 40 acc: 0.77 loss: 0.54 index: 60 acc: 0.78 loss: 1.45 epoch: 1 acc: 0.78 loss: 0.49 index: 0 acc: 0.84 ...<\/code><\/pre>\n<p>\u041c\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0431\u043e\u043b\u044c\u0448\u0443\u044e \u0440\u0430\u0431\u043e\u0442\u0443! \u0414\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u043c\u043d\u043e\u0433\u043e \u0441\u043b\u043e\u0435\u0432, \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u043e\u0432 \u0433\u0440\u0430\u0434\u0438\u0435\u043d\u0442\u043e\u0433\u043e \u0441\u043f\u0443\u0441\u043a\u0430 \u0438 \u043e\u0431\u0443\u0447\u0438\u043b\u0438 \u0441\u0432\u0451\u0440\u0442\u043e\u0447\u043d\u0443\u044e \u043c\u043e\u0434\u0435\u043b\u044c \u0434\u043b\u044f \u0437\u0430\u0434\u0430\u0447\u0438 \u043a\u043b\u0430\u0441\u0441\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439!<\/p>\n<p>\u0412\u043e\u0442 \u0438 \u043d\u0430 \u044d\u0442\u043e\u043c \u0432\u0442\u043e\u0440\u0430\u044f \u0447\u0430\u0441\u0442\u044c \u043f\u043e\u0434\u043e\u0448\u043b\u0430 \u043a \u043a\u043e\u043d\u0446\u0443. \u041f\u043e\u043b\u043d\u044b\u0439 \u043a\u043e\u0434 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438\u00a0<a href=\"https:\/\/www.kaggle.com\/code\/freacle\/notebook981215b8da\" rel=\"noopener noreferrer nofollow\">\u0442\u0443\u0442<\/a><\/p>\n<p>\u0412 \u0442\u0440\u0435\u0442\u044c\u0435\u0439 \u0447\u0430\u0441\u0442\u0438 \u044f \u043f\u043b\u0430\u043d\u0438\u0440\u0443\u044e:<\/p>\n<ul>\n<li>\n<p>\u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u0438\u0442\u044c \u0430\u043d\u0430\u043b\u043e\u0433 <code>pytorch.tensor()<\/code>\u00a0  <\/p>\n<\/li>\n<li>\n<p>\u043f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438 \u0432\u0441\u0435 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u043d\u0430 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0433\u0440\u0430\u0444\u00a0  <\/p>\n<\/li>\n<li>\n<p>\u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c\u00a0<strong>Embedding<\/strong>,\u00a0<strong>LayerNorm<\/strong>,\u00a0<strong>ModuleList<\/strong>\u00a0  <\/p>\n<\/li>\n<li>\n<p>\u043f\u0440\u043e\u0432\u0435\u0441\u0442\u0438 \u0440\u0435\u0444\u0430\u043a\u0442\u043e\u0440\u0438\u043d\u0433 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438\u00a0  <\/p>\n<\/li>\n<li>\n<p>\u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u0435\u0440\u0435\u043d\u043e\u0441 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0439 \u043d\u0430 gpu  <\/p>\n<\/li>\n<li>\n<p>\u043d\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u043d\u0430 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435 gpt2-1.5\u0412 \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u0435\u0433\u043e<\/p>\n<\/li>\n<\/ul>\n<p><a href=\"https:\/\/github.com\/TimaGitHub\/pycandle\" rel=\"noopener noreferrer nofollow\">\u041f\u0435\u0440\u0432\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438<\/a><\/p>\n<p><a href=\"https:\/\/github.com\/TimaGitHub\/pycandle-2\" rel=\"noopener noreferrer nofollow\">\u0412\u0442\u043e\u0440\u0430\u044f \u0432\u0435\u0440\u0441\u0438\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438<\/a><\/p>\n<p><a href=\"https:\/\/github.com\/TimaGitHub\/smolGPT\" rel=\"noopener noreferrer nofollow\">GPT-2 \u043d\u0430 \u044d\u0442\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435<\/a><\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/869520\/\"> https:\/\/habr.com\/ru\/articles\/869520\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p><strong>PyTorch<\/strong>\u00a0\u2014 \u044d\u0442\u043e \u043c\u043e\u0449\u043d\u044b\u0439 \u0438 \u0433\u0438\u0431\u043a\u0438\u0439 \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a \u0434\u043b\u044f \u043c\u0430\u0448\u0438\u043d\u043d\u043e\u0433\u043e \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f, \u0448\u0438\u0440\u043e\u043a\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0439 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u0435\u0439\u0440\u043e\u043d\u043d\u044b\u0445 \u0441\u0435\u0442\u0435\u0439. \u041e\u043d \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u0435\u043d \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u043f\u0440\u043e\u0441\u0442\u043e\u0442\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f, \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u043c \u0432\u044b\u0447\u0438\u0441\u043b\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u043c \u0433\u0440\u0430\u0444\u0430\u043c \u0438 \u0431\u043e\u0433\u0430\u0442\u043e\u0439 \u044d\u043a\u043e\u0441\u0438\u0441\u0442\u0435\u043c\u0435 \u0438\u043d\u0441\u0442\u0440\u0443\u043c\u0435\u043d\u0442\u043e\u0432 \u0434\u043b\u044f \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f \u043c\u043e\u0434\u0435\u043b\u0435\u0439. \u0414\u043b\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u044d\u0442\u043e\u0433\u043e \u0444\u0440\u0435\u0439\u043c\u0432\u043e\u0440\u043a\u0430, \u0447\u0430\u0441\u0442\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u043e\u0432\u0435\u0440\u0445\u043d\u043e\u0441\u0442\u043d\u043e \u043f\u043e\u043d\u0438\u043c\u0430\u0442\u044c \u0440\u0430\u0431\u043e\u0442\u0443 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u043e\u0432 \u043c\u0430\u0448\u0438\u043d\u043d\u043e\u0433\u043e \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f.<\/p>\n<p>\u041d\u043e \u0410\u043d\u0434\u0440\u0435\u0439 \u041a\u0430\u0440\u043f\u0430\u0442\u044b, \u0438\u0437\u0432\u0435\u0441\u0442\u043d\u044b\u0439 \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0432 \u043e\u0431\u043b\u0430\u0441\u0442\u0438 \u0418\u0418, \u0441\u0447\u0438\u0442\u0430\u0435\u0442, \u0447\u0442\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u043e\u0432 \u0441 \u043d\u0443\u043b\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u043d\u044f\u0442\u044c \u0438\u0445 \u0441\u0443\u0442\u044c \u0438 \u0434\u0435\u0442\u0430\u043b\u0438 \u0440\u0430\u0431\u043e\u0442\u044b, \u0447\u0442\u043e \u0441\u043b\u043e\u0436\u043d\u043e \u043e\u0441\u043e\u0437\u043d\u0430\u0442\u044c, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0442\u043e\u043b\u044c\u043a\u043e \u0433\u043e\u0442\u043e\u0432\u044b\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438. \u042d\u0442\u043e \u043f\u043e\u043c\u043e\u0433\u0430\u0435\u0442 \u0440\u0430\u0437\u0432\u0438\u0442\u044c \u0438\u043d\u0442\u0443\u0438\u0446\u0438\u044e \u0434\u043b\u044f \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u0433\u043e \u043f\u0440\u0438\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0438 \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f \u043c\u0435\u0442\u043e\u0434\u043e\u0432. \u0410\u043d\u0434\u0440\u0435\u0439 \u043f\u043e\u0441\u0432\u044f\u0449\u0430\u0435\u0442 \u043c\u043d\u043e\u0433\u043e \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0433\u043e \u0432\u0440\u0435\u043c\u0435\u043d\u0438, \u0447\u0442\u043e\u0431\u044b \u043e\u0431\u044a\u044f\u0441\u043d\u044f\u0442\u044c \u043a\u043b\u044e\u0447\u0435\u0432\u044b\u0435 \u043f\u0440\u0438\u043d\u0446\u0438\u043f\u044b \u0440\u0430\u0431\u043e\u0442\u044b \u043d\u0435\u0439\u0440\u043e\u0441\u0435\u0442\u0435\u0439 \u0432 \u0441\u0432\u043e\u0438\u0445\u00a0<a href=\"https:\/\/karpathy.ai\/\" rel=\"noopener noreferrer nofollow\">\u0431\u043b\u043e\u0433\u0430\u0445<\/a>\u00a0\u0438 \u043d\u0430 \u0441\u0432\u043e\u0451\u043c\u00a0<a href=\"https:\/\/www.youtube.com\/@AndrejKarpathy\" rel=\"noopener noreferrer nofollow\">\u044e\u0442\u0443\u0431-\u043a\u0430\u043d\u0430\u043b\u0435<\/a>. \u041e\u043d \u0442\u0430\u043a\u0436\u0435 \u043d\u0435 \u0440\u0430\u0437 \u043f\u043e\u0434\u0447\u0435\u0440\u043a\u0438\u0432\u0430\u043b, \u0447\u0442\u043e \u043d\u0430 \u0435\u0433\u043e \u043a\u0443\u0440\u0441\u0435 \u0432\u00a0<a href=\"https:\/\/cs.stanford.edu\/people\/karpathy\/\" rel=\"noopener noreferrer nofollow\">C\u0442\u044d\u043d\u0444\u043e\u0440\u0434\u0435<\/a>\u00a0\u0435\u0441\u0442\u044c \u0437\u0430\u0434\u0430\u0447\u0438 \u043f\u043e \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438 \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0445 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u043e\u0432, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043e\u0431\u0440\u0430\u0442\u043d\u043e\u0435 \u0440\u0430\u0441\u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0435\u043d\u0438\u0435.<\/p>\n<p>\u042f \u0445\u043e\u0442\u0435\u043b \u0431\u044b \u043f\u043e\u0441\u0432\u044f\u0442\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u0443\u044e \u0441\u0442\u0430\u0442\u044c\u044e \u044d\u0442\u043e\u0439 \u0438\u0434\u0435\u0438, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043c\u043d\u0435 \u0441\u0430\u043c\u043e\u043c\u0443 \u043e\u0441\u043e\u0431\u0435\u043d\u043d\u043e \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e \u043a\u043e\u043f\u0430\u0442\u044c\u0441\u044f \u0432 \u0430\u043b\u0433\u043e\u0440\u0438\u0442\u043c\u0430\u0445 \u0433\u043b\u0443\u0431\u043e\u043a\u043e\u0433\u043e \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u044f. \u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0435\u043d\u0438\u0435 <a href=\"https:\/\/habr.com\/ru\/articles\/869118\/\" rel=\"noopener noreferrer nofollow\">\u043f\u0435\u0440\u0432\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438<\/a><\/p>\n<p>\u0421\u0435\u0433\u043e\u0434\u043d\u044f \u043c\u044b:<\/p>\n<ul>\n<li>\n<p>\u0434\u043e\u0431\u0430\u0432\u0438\u043c\u00a0<strong>CNN<\/strong>,\u00a0<strong>BatchNorm<\/strong>,\u00a0<strong>MaxPool<\/strong>,\u00a0<strong>MinPool<\/strong><\/p>\n<\/li>\n<li>\n<p>\u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u043c\u00a0<strong>RMSProp<\/strong>,\u00a0<strong>NaG<\/strong>,\u00a0<strong>Adam<\/strong><\/p>\n<\/li>\n<li>\n<p>\u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0440\u0435\u0433\u0443\u043b\u044f\u0440\u0438\u0437\u0430\u0446\u0438\u044e \u0432 loss-\u0444\u0443\u043d\u043a\u0446\u0438\u044e<\/p>\n<\/li>\n<li>\n<p>\u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u043e\u0432\u044b\u0435 \u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438<\/p>\n<\/li>\n<li>\n<p>\u043d\u0430\u043f\u0438\u0448\u0435\u043c\u00a0<strong>DataLoader<\/strong><\/p>\n<\/li>\n<\/ul>\n<p>\u041f\u043e\u0435\u0445\u0430\u043b\u0438!<\/p>\n<p>\u041c\u044b \u043e\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043b\u0438\u0441\u044c \u043d\u0430 \u0442\u043e\u043c, \u0447\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043f\u043e\u0445\u043e\u0436\u0443\u044e \u043d\u0430 <strong>PyTorch<\/strong> \u043e\u0431\u0435\u0440\u0442\u043a\u0443.<\/p>\n<pre><code class=\"python\">loss_fn = CrossEntropyLoss() model = SimpleNet() optim = SGD(model.parameters(), learning_rate = 0.01)  for i in range(100):     output = model(input_x)     loss = loss_fn(output, target_x)     loss.backward()     optim.step()<\/code><\/pre>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0442\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u043f\u044b\u0442\u0430\u0435\u043c\u0441\u044f \u043e\u0431\u0443\u0447\u0438\u0442\u044c \u043d\u0430 \u043a\u0430\u043a\u0438\u0445-\u043d\u0438\u0431\u0443\u0434\u044c \u0440\u0435\u0430\u043b\u044c\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445! \u0414\u043b\u044f \u044d\u0442\u043e\u0439 \u0437\u0430\u0434\u0430\u0447\u0438 \u043d\u0430\u043c \u043f\u043e\u0434\u043e\u0439\u0434\u0435\u0442 \u043d\u0430\u0431\u043e\u0440 \u0440\u0443\u043a\u043e\u043f\u0438\u0441\u043d\u044b\u0445 \u0446\u0438\u0444\u0440 MNIST. \u041d\u043e \u043a\u0430\u043a \u043d\u0430\u043c \u044d\u0444\u0444\u0435\u043a\u0442\u0438\u0432\u043d\u043e \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432\u0435\u0441\u044c \u0434\u0430\u0442\u0430\u0441\u0435\u0442? \u0412\u0441\u043f\u043e\u043c\u043d\u0438\u043c \u043a\u0430\u043a \u044d\u0442\u043e \u0434\u0435\u043b\u0430\u0435\u0442\u0441\u044f \u0432 <strong>PyTorch<\/strong>.  \u041c\u044b \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043e\u0431\u044a\u0435\u043a\u0442 \u043a\u043b\u0430\u0441\u0441\u0430 <strong>DataLoader<\/strong> &#8212; \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0440\u0430\u0437\u0431\u0438\u0442\u044c \u0432\u0435\u0441\u044c \u0434\u0430\u0442\u0430\u0441\u0435\u0442 \u043d\u0430 \u0431\u0430\u0442\u0447\u0438 \u0438 \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043a\u0430\u043a \u0438\u0442\u0435\u0440\u0438\u0440\u0443\u0435\u043c\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0438\u043c \u0441\u043e\u0437\u0434\u0430\u0432\u0430\u0442\u044c \u0441\u043e\u0431\u0441\u0442\u0432\u0435\u043d\u043d\u044b\u0435 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438.<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u0441\u0441\u044b\u043b\u043a\u0443 \u043d\u0430 \u043d\u0430\u0448 \u043d\u0430\u0431\u043e\u0440 \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439 <code>dataset<\/code>.  \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440<\/p>\n<pre><code class=\"python\">import pandas as pd import numpy as np data_pd = pd.read_csv(path) dataset = np.array(data_pd).astype(float) np.random.shuffle(dataset) # \u043f\u0435\u0440\u0435\u043c\u0435\u0448\u0430\u0435\u043c \u0434\u0430\u0442\u0430\u0441\u0435\u0442 dataset.shape &gt;&gt;&gt; (5000, 785) # 1 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043a\u043b\u0430\u0441\u0441\u0430 + 784 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u0438\u0441\u043a\u0435\u043b\u0435\u0439<\/code><\/pre>\n<h4>Dataloader<\/h4>\n<pre><code class=\"python\">class DataLoader():     def __init__(self, data, batch_size=64, shuffle=True, flatten = True):         self.data = data         self.index = 0 # \u0438\u043d\u0434\u0435\u043a\u0441 \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e\u0439 \u0438\u0442\u0435\u0440\u0430\u0446\u0438\u0438         self.items = [] # \u0437\u0434\u0435\u0441\u044c \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043d\u0430\u0431\u043e\u0440\u044b \u0431\u0430\u0442\u0447\u0435\u0439         self.flatten = flatten  # \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u043c \u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0430\u0437 \u0441\u043c\u043e\u0436\u0435\u043c \u043f\u0440\u043e\u0438\u0442\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0432\u0435\u0441\u044c \u043d\u0430\u0431\u043e\u0440         self.max = data.shape[0] \/\/ batch_size + (1 if data.shape[0] % batch_size != 0 else 0)          if shuffle == True:         # \u043c\u0435\u0448\u0430\u0435\u043c \u043d\u0430\u0431\u043e\u0440 \u0435\u0441\u043b\u0438 \u0437\u0430\u0445\u043e\u0442\u0435\u043b \u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u0435\u043b\u044c             self.data = np.random.permutation(self.data) # \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0441\u043f\u0438\u0441\u043e\u043a \u0432\u0441\u0435\u0445 \u0431\u0430\u0442\u0447\u0435\u0439         for _ in range(self.max):             self.items.append(self.data[batch_size * _: batch_size * (_ + 1)])                  # \u043f\u0440\u0435\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0432 \u0438\u0442\u0435\u0440\u0438\u0440\u0443\u0435\u043c\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 def __iter__(self):         return self      # \u0434\u043b\u044f \u0446\u0438\u043a\u043b\u043e\u0432 for \u0438 while     def __next__(self):         if self.index &lt; self.max:             value = self.items[self.index] # \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0431\u0430\u0442\u0447             self.index += 1             if self.flatten:                  # \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u0432 \u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043b\u0438\u0431\u043e ((64, 784), (64, 1))                 return value[:, 1:], value[:, 0].reshape(-1, 1)             else:         # \u043b\u0438\u0431\u043e ((64, 28, 28), (64, 1)), \u0442\u043e \u0435\u0441\u0442\u044c \u043a\u0430\u043a \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435                 return value[:, 1:].reshape(value.shape[0], 28, 28), value[:, 0].reshape(-1, 1)         else:             self.index = 0             raise StopIteration # \u0432\u044b\u0445\u043e\u0434\u0438\u043c \u0438\u0437 \u0446\u0438\u043a\u043b\u0430<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0432\u0441\u044f \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u043d\u0430\u0431\u043e\u0440\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u043f\u043e \u0431\u0430\u0442\u0447\u0430\u043c \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<pre><code class=\"python\">data_loader = DataLoader(dataset, batch_size=64, shuffle=True, flatten=True)<\/code><\/pre>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u043d\u0430\u0448 \u0431\u0430\u0442\u0447.<\/p>\n<pre><code class=\"python\">for x, target in dataloader: break target.shape &gt;&gt;&gt; (64, 1)<\/code><\/pre>\n<p>\u0421\u0442\u0440\u0430\u043d\u043d\u043e, \u0443 \u043d\u0430\u0441 \u0432\u0440\u043e\u0434\u0435 10 \u043a\u043b\u0430\u0441\u0441\u043e\u0432, \u0430 \u0440\u0430\u0437\u043c\u0435\u0440 \u043f\u043e\u0447\u0435\u043c\u0443-\u0442\u043e 1. \u0417\u0430\u0433\u043b\u044f\u043d\u0435\u043c \u0432\u043d\u0443\u0442\u0440\u044c<\/p>\n<pre><code class=\"python\">target[0] &gt;&gt;&gt; array([8.])<\/code><\/pre>\n<p>\u042d\u0442\u043e \u043d\u043e\u043c\u0435\u0440 \u043a\u043b\u0430\u0441\u0441\u0430, \u0430 \u043c\u044b \u0432\u0435\u0434\u044c \u0436\u0434\u0430\u043b\u0438, \u0447\u0442\u043e \u0431\u0443\u0434\u0435\u0442 <code>[0, 0, 0, 0, 0, 0, 0, 0, 1, 0]<\/code><br \/>\u0422\u043e\u0433\u0434\u0430 \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u0442\u0430\u043a\u043e\u0439 \u0441\u043b\u0443\u0447\u0430\u0439 \u0432\u043d\u0443\u0442\u0440\u0438 \u043d\u0430\u0448\u0435\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430 <code>CrossEntropyLoss<\/code><\/p>\n<pre><code class=\"python\">class CrossEntropyLoss:      def __call__(self, logits, true):         predicted = np.exp(logits) \/ np.sum(np.exp(logits), axis=1).reshape(-1, 1) # softmax         self.predicted = np.array(predicted, copy=True) # \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043a\u043e\u043f\u0438\u044e \u0432\u0445\u043e\u0434\u043d\u044b\u0445 \u043c\u0430\u0442\u0440\u0438\u0446\u044b \u0434\u043b\u044f \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0438\u0445 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u0439         ### \u0414\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u044d\u0442\u0438 \u0441\u0442\u0440\u043e\u0447\u043a\u0438         number_of_classes = predicted.shape[1] self.true = np.int_(np.arange(0, number_of_classes) == true)  ###         # \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043b\u043e\u0441\u0441-\u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u043f\u0440\u044f\u043c\u043e \u043f\u043e \u0444\u043e\u0440\u043c\u0443\u043b\u0435         self.loss = -1 * np.sum(self.true * np.log(self.predicted + 1e-5), axis=1)         return self<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u043e\u0431\u0443\u0447\u0438\u0442\u044c \u043d\u0430\u0448\u0443 \u043d\u0435\u0439\u0440\u043e\u043d\u043a\u0443. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043c\u043e\u0434\u0435\u043b\u044c<\/p>\n<pre><code class=\"python\">class SimpleNet(Module):     def __init__(self):         super().__init__()         self.linear1 = Linear(input_channels=784, output_channels=200, bias=True)         self.linear2 = Linear(input_channels=200, output_channels=50, bias=True)         self.linear3 = Linear(input_channels=50, output_channels=10, bias=True)         self.relu = ReLU()      def forward(self, x):         x = self.linear1(x)         x = self.relu(x)         x = self.linear2(x)         x = self.relu(x)         x = self.linear3(x)         return x<\/code><\/pre>\n<p>\u0418\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0438\u0440\u0443\u0435\u043c \u0432\u0441\u0451 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e\u0435<\/p>\n<pre><code class=\"python\">loss_fn = CrossEntropyLoss() model = SimpleNet() optim = SGD(model.parameters(), learning_rate = 0.01)<\/code><\/pre>\n<p>\u0414\u043b\u044f \u043d\u0430\u0448\u0435\u0433\u043e \u043d\u0430\u0431\u043e\u0440\u0430 \u0432 5000 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u043e\u0432 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f <code>5000 \/\/ 64 = 78 \u0431\u0430\u0442\u0447\u0435\u0439<\/code>. \u041f\u0440\u043e\u0439\u0434\u0435\u043c\u0441\u044f, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, 5 \u0440\u0430\u0437 \u043f\u043e \u043d\u0430\u0448\u0435\u043c\u0443 \u0434\u0430\u0442\u0430\u0441\u0435\u0442\u0443, \u0442\u043e \u0435\u0441\u0442\u044c \u0443 \u043d\u0430\u0441 \u0431\u0443\u0434\u0435\u0442 5 \u044d\u043f\u043e\u0445.<\/p>\n<pre><code class=\"python\">for i in range(5):     for batch in data_loader:         input_x, target = batch         input_x = input_x \/ 255 # \u043d\u043e\u0440\u043c\u0438\u0440\u0443\u0435\u043c \u043d\u0430 [0, 1], \u0438\u043d\u0430\u0447\u0435 \u043e\u0431\u0443\u0447\u0435\u043d\u0438\u0435 \u043c\u043e\u0436\u0435\u0442 \u0441\u0442\u0430\u0442\u044c \u043d\u0435\u0441\u0442\u0430\u0431\u0438\u043b\u044c\u043d\u044b\u043c         output = model(input_x)         loss = loss_fn(output, target)         loss.backward()         optim.step()     print(loss.loss.mean(), i) # \u0411\u0435\u0440\u0435\u043c \u0441\u0440\u0435\u0434\u043d\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043b\u043e\u0441\u0441\u0430 \u043f\u043e \u0431\u0430\u0442\u0447\u0443  &gt;&gt;&gt;0.622 0 0.294 1 0.166 2 0.101 3 0.070 4<\/code><\/pre>\n<p>\u0417\u043d\u0430\u0447\u0435\u043d\u0438\u0435 <strong>loss<\/strong>-\u0444\u0443\u043d\u043a\u0446\u0438\u0438 \u0443\u043c\u0435\u043d\u044c\u0448\u0430\u0435\u0442\u0441\u044f, \u0437\u043d\u0430\u0447\u0438\u0442 \u043d\u0430\u0448\u0430 \u043c\u043e\u0434\u0435\u043b\u044c \u043e\u0431\u0443\u0447\u0430\u0435\u0442\u0441\u044f \u043d\u0430 \u043d\u0430\u0448\u0435\u043c \u043d\u0430\u0431\u043e\u0440\u0435 \u0434\u0430\u043d\u043d\u044b\u0445!<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0439\u0434\u0435\u043c \u0434\u0430\u043b\u044c\u0448\u0435 \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u043e\u0432\u044b\u0435 \u0441\u043b\u043e\u0438! \u0412 \u043f\u0435\u0440\u0432\u0443\u044e \u043e\u0447\u0435\u0440\u0435\u0434\u044c \u044f \u0445\u043e\u0442\u0435\u043b \u0431\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0432\u0451\u0440\u0442\u043e\u0447\u043d\u044b\u0435 \u0441\u043b\u043e\u0438, \u0442\u0430\u043a \u043a\u0430\u043a \u0438\u043c\u0435\u043d\u043d\u043e \u043e\u043d\u0438 \u0445\u043e\u0440\u043e\u0448\u043e \u0441\u043f\u0440\u0430\u0432\u043b\u044f\u044e\u0442\u0441\u044f \u0441 \u0441\u043b\u043e\u0436\u043d\u044b\u043c\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\u043c\u0438. \u0422\u043e\u043b\u044c\u043a\u043e \u043b\u0438\u0448\u044c \u043d\u0430 \u043b\u0438\u043d\u0435\u0439\u043d\u044b\u0445 \u0441\u043b\u043e\u044f\u0445 \u043c\u044b \u0434\u0430\u043b\u0435\u043a\u043e \u043d\u0435 \u0443\u0439\u0434\u0435\u043c.<\/p>\n<h2>CNN<\/h2>\n<p>\u041e\u043f\u0435\u0440\u0430\u0446\u0438\u044f \u0441\u0432\u0435\u0440\u0442\u043a\u0438 \u0441\u043e\u0441\u0442\u043e\u0438\u0442 \u0432 \u0441\u0432\u043e\u0440\u0430\u0447\u0438\u0432\u0430\u043d\u0438\u0438 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0444\u0438\u043b\u044c\u0442\u0440\u0430.\u00a0<strong>\u0424\u0438\u043b\u044c\u0442\u0440<\/strong>\u00a0\u0438\u043b\u0438 <strong>kernel<\/strong> \u2014 \u044d\u0442\u043e \u0442\u043e\u0436\u0435 \u043c\u0430\u0442\u0440\u0438\u0446\u0430 \u0447\u0438\u0441\u0435\u043b.<br \/>\u0421\u0432\u0435\u0440\u0442\u043a\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0440\u0430\u0437\u043c\u0435\u0440\u0430 4\u04454 \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u043c \u0440\u0430\u0437\u043c\u0435\u0440\u0430 3\u04453: \u041f\u043e\u043b\u0443\u0447\u0435\u043d\u043d\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f\u00a0<strong>\u043a\u0430\u0440\u0442\u043e\u0439 \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438<\/strong>. \u0421\u0432\u0435\u0440\u0442\u043a\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0448\u0430\u0433 \u0437\u0430 \u0448\u0430\u0433\u043e\u043c: <\/p>\n<figure class=\"full-width\">\n<div><figcaption>Convolution<\/figcaption><\/div>\n<\/figure>\n<p>\u0417\u0430\u043c\u0435\u0442\u0438\u043c, \u0447\u0442\u043e \u0440\u0430\u0437\u043c\u0435\u0440 \u043a\u0430\u0440\u0442\u044b \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438 \u043f\u043e\u0441\u043b\u0435 \u0441\u0432\u0435\u0440\u0442\u043a\u0438 \u0441\u0442\u0430\u043b \u043c\u0435\u043d\u044c\u0448\u0435, \u0447\u0435\u043c \u0440\u0430\u0437\u043c\u0435\u0440 \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0433\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f. \u041e\u0431\u0449\u0430\u044f \u0444\u043e\u0440\u043c\u0443\u043b\u0430 \u0440\u0430\u0437\u043c\u0435\u0440\u0430 \u043a\u0430\u0440\u0442\u044b \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438 \u0442\u0430\u043a\u0430\u044f:<\/p>\n<p><code>m = (i \u2212 f + 2 * padding) \/ stride + 1<\/code>,<br \/> \u0433\u0434\u0435:<\/p>\n<ul>\n<li>\n<p><code>m<\/code> \u2014 \u0440\u0430\u0437\u043c\u0435\u0440 \u043a\u0430\u0440\u0442\u044b \u0430\u043a\u0442\u0438\u0432\u0430\u0446\u0438\u0438;<\/p>\n<\/li>\n<li>\n<p><code>i<\/code> \u2014 \u0440\u0430\u0437\u043c\u0435\u0440 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f;<\/p>\n<\/li>\n<li>\n<p><code>f<\/code> \u2014 \u0440\u0430\u0437\u043c\u0435\u0440 \u0444\u0438\u043b\u044c\u0442\u0440\u0430.<\/p>\n<\/li>\n<li>\n<p><code>padding<\/code> \u2014 \u043e\u0442\u0441\u0442\u0443\u043f \u043e\u0442 \u043a\u0440\u0430\u044f<\/p>\n<\/li>\n<li>\n<p><code>stride<\/code> \u2014 \u0448\u0430\u0433 \u0444\u0438\u043b\u044c\u0442\u0440\u0430<\/p>\n<\/li>\n<\/ul>\n<p>\u0420\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441 <a href=\"https:\/\/colab.research.google.com\/drive\/1ZMu6C3ZEt3kCSDBNWGM6sicXL5-EhSve?usp=sharing#scrollTo=JpTe1vvMkTmG\" rel=\"noopener noreferrer nofollow\">\u0445\u043e\u0440\u043e\u0448\u0438\u043c \u043d\u043e\u0443\u0442\u0431\u0443\u043a\u043e\u043c<\/a>, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043d\u044f\u0442\u044c \u0438\u043d\u0442\u0443\u0438\u0446\u0438\u044e \u0441\u0432\u0451\u0440\u0442\u043e\u043a<\/p>\n<p>\u041a\u0430\u043a \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0441\u0432\u0451\u0440\u0442\u043e\u0447\u043d\u0430\u044f \u0441\u0435\u0442\u044c \u0434\u043b\u044f \u043a\u043b\u0430\u0441\u0441\u0438\u0444\u0438\u043a\u0430\u0446\u0438\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a:<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u0423 \u043d\u0430\u0441 \u0443\u0436\u0435 \u0435\u0441\u0442\u044c \u0441\u043b\u043e\u0438 <code>Flatten<\/code> \u0438 <code>Linear<\/code>. \u041e\u0441\u0442\u0430\u0435\u0442\u0441\u044f \u0442\u043e\u043b\u044c\u043a\u043e <code>Conv2d<\/code>.<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0440\u043e\u0441\u0442\u0443\u044e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u0441\u0432\u0435\u0440\u0442\u043a\u0438 \u043e\u0434\u043d\u043e\u0439 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u0444\u0438\u043b\u044c\u0442\u0440\u0430.<\/p>\n<pre><code class=\"python\">image = np.array([[0, 50, 0, 29],         [0, 80, 31, 2],          [33, 90, 0, 75],         [0, 9, 0, 95]         ]) kernel = np.ones((3, 3))  image, kernel &gt;&gt;&gt;array([[ 0, 50,  0, 29],         [ 0, 80, 31,  2],         [33, 90,  0, 75],         [ 0,  9,  0, 95]]),  array([[1., 1., 1.],         [1., 1., 1.],         [1., 1., 1.]])<\/code><\/pre>\n<pre><code class=\"python\">i = image.shape[0] f = kernel.shape[0] padding = 0 step = 1 m = (i - f + 2*padding) \/\/ step + 1 &gt;&gt;&gt; 2<\/code><\/pre>\n<pre><code class=\"python\">new_image = np.zeros((m, m))  for y in range(m):     for x in range(m):         start_x = x * step         end_x = start_x + f         start_y = y * step         end_y = start_y + f         new_image[y][x] = np.sum(image[start_y:end_y, start_x:end_x] * kernel) new_image &gt;&gt;&gt; array([[284., 357.],        [243., 382.]])<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c \u0441 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u0447\u043d\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0435\u0439.<\/p>\n<pre><code class=\"python\">import scipy.signal scipy.signal.fftconvolve(image, kernel, mode='valid') &gt;&gt;&gt;array([[284., 357.],        [243., 382.]])<\/code><\/pre>\n<p>\u0412 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u0441\u0432\u0451\u0440\u0442\u043a\u0438 \u0438\u043c\u0435\u043d\u043d\u043e <code>scipy.signal.fftconvolve<\/code>, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u044d\u0442\u043e \u0432\u0435\u0441\u044c\u043c\u0430 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0435\u043c\u043a\u0430\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044f, \u0430 \u0434\u0430\u043d\u043d\u044b\u0439 <a href=\"https:\/\/docs.scipy.org\/doc\/scipy\/reference\/generated\/scipy.signal.fftconvolve.html\" rel=\"noopener noreferrer nofollow\">\u043c\u0435\u0442\u043e\u0434<\/a> \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u043f\u043e\u0441\u0447\u0438\u0442\u0430\u0442\u044c \u0442\u043e\u0436\u0435 \u0441\u0430\u043c\u043e\u0435 \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0431\u044b\u0441\u0442\u0440\u043e\u0433\u043e \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0424\u0443\u0440\u044c\u0435!<br \/>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u043f\u0440\u043e\u0431\u0443\u0435\u043c \u0446\u0432\u0435\u0442\u043d\u0443\u044e \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443, \u0442\u043e \u0435\u0441\u0442\u044c \u043d\u0435 <code>(height, width)<\/code>, \u0430 <code>(channels, height, width)<\/code><br \/><em>\u0421\u043f\u043e\u0439\u043b\u0435\u0440<\/em>: \u0435\u0441\u0442\u044c \u0434\u0432\u0430 \u0441\u043f\u043e\u0441\u043e\u0431\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u044c \u0441\u0432\u0435\u0440\u0442\u043a\u0443 \u043c\u043d\u043e\u0433\u043e\u043a\u0430\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043f\u0435\u0440\u0432\u044b\u0439 \u043c\u0435\u0442\u043e\u0434, \u0442\u0430\u043a \u043a\u0430\u043a \u043e\u043d \u043d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0447\u0430\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c\u044b\u0439.<br \/>\u0421\u0433\u0435\u043d\u0435\u0440\u0438\u0440\u0443\u0435\u043c \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443 \u0438 \u0444\u0438\u043b\u044c\u0442\u0440<\/p>\n<pre><code class=\"python\">image = np.random.randn(3, 7, 7) kernel = np.ones((3, 3, 3))<\/code><\/pre>\n<p>\u0420\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438 \u043f\u043e\u0447\u0442\u0438 \u043d\u0438\u043a\u0430\u043a \u043d\u0435 \u043f\u043e\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f<\/p>\n<pre><code class=\"python\">i = image.shape[1] f = kernel.shape[1] padding = 0 step = 1 m = (i-f + 2*padding) \/\/ step +1 new_image = np.zeros((m, m)) for y in range(m):     for x in range(m):         start_x = x * step         end_x = start_x + f         start_y = y * step         end_y = start_y + f         new_image[y][x] = np.sum(image[:, start_y:end_y, start_x:end_x] * kernel)<\/code><\/pre>\n<p>\u0421\u0440\u0430\u0432\u043d\u0438\u0432\u0430\u0435\u043c!<\/p>\n<pre><code class=\"python\">np.allclose(new_image, scipy.signal.fftconvolve(image, kernel, mode='valid')) &gt;&gt;&gt; True<\/code><\/pre>\n<p>\u041a\u0440\u0443\u0442\u043e!<br \/> \u0422\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u044e \u0441\u0432\u0451\u0440\u0442\u043a\u0438 \u043d\u0430 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430\u0445 \u043e\u0434\u043d\u0438\u043c \u0444\u0438\u043b\u044c\u0442\u0440\u043e\u043c. \u041e\u043f\u044f\u0442\u044c \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0443\u0441\u043b\u043e\u0436\u043d\u0438\u043c \u043a\u043e\u0434<\/p>\n<pre><code class=\"python\">image = np.random.randn(10, 3, 7, 7) kernel = np.ones((1, 3, 3, 3))  i = image.shape[-1] f = kernel.shape[-1] padding = 0 step = 1 m = (i-f + 2*padding) \/\/ step +1 number_of_images = image.shape[0] new_image = np.zeros((number_of_images, m, m)) for image_n in range(number_of_images):     for y in range(m):         for x in range(m):             start_x = x * step             end_x = start_x + f             start_y = y * step             end_y = start_y + f             new_image[image_n][y][x] = np.sum(image[image_n,:, start_y:end_y, start_x:end_x] * kernel)<\/code><\/pre>\n<p>\u041f\u0440\u043e\u0432\u0435\u0440\u0438\u043c<\/p>\n<pre><code class=\"python\">np.allclose(new_image, scipy.signal.fftconvolve(image, kernel, mode='valid').squeeze(axis=1)) &gt;&gt;&gt; True<\/code><\/pre>\n<p>\u0423\u0440\u0430, \u043f\u043e\u043b\u0443\u0447\u0438\u043b\u043e\u0441\u044c, \u0442\u0435\u043f\u0435\u0440\u044c \u0434\u0430\u0432\u0430\u0439\u0442\u0435 \u043d\u0430\u043e\u0431\u043e\u0440\u043e\u0442, \u043f\u0440\u043e\u0439\u0434\u0435\u043c\u0441\u044f \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u0444\u0438\u043b\u044c\u0442\u0440\u0430\u043c\u0438 \u043f\u043e \u043e\u0434\u043d\u043e\u0439 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0435!<\/p>\n<pre><code class=\"python\">image = np.random.randn(1, 3, 7, 7)<\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-443024","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/443024","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=443024"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/443024\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=443024"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=443024"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=443024"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}