def forward(self, X: Variable): """ :param X: X is a 4d tensor, [batch, channel, row, col] # TODO add channel in different places :return: """ # print("starting convolution.") def idx_three2one(idx, shape): new_idx = idx[0] * np.prod(shape[1:]) + idx[1] * shape[2] + idx[2] return new_idx # Notice that we only need to calculate mapping once for all epochs if self.initialize: self.n, self.in_channel, self.x, self.y = X.shape # We first calculate the new matrix size. self.x_new = int((self.x - self.kernel_size[0] + 2 * self.padding[0]) / self.stride[0] + 1) self.y_new = int((self.y - self.kernel_size[1] + 2 * self.padding[1]) / self.stride[1] + 1) self.old_length = self.in_channel * self.x * self.y self.new_length = self.out_channel * self.x_new * self.y_new # The thing about mapping is that we have to calculate the mapping during each iteration, # Because w has changed within each iteration # On the other hand, we have to keep w changing at the same time. # We only need to initialize b once, with the knowledge of xnew and ynew # Initialize the kernel self.w = Variable(np.random.normal(0, 0.01, (self.out_channel, self.in_channel, self.kernel_size[0], self.kernel_size[1])), trainable=self.trainable) self.b = Variable(np.random.normal(0, 0.01, (1, self.out_channel, self.x_new, self.y_new)), trainable=self.trainable, param_share=True) ''' Now we create a w2mapping, the mapping itself we only need it once for all. After we know the mapping, we can easily do the back-prop and forward-prop each time. ''' self.w2mapping = [] # Logic 1, without sorting for filter_idx in range(self.out_channel): for i in range(self.x_new): for j in range(self.y_new): # Index for new matrix mapping_new = idx_three2one((filter_idx, i, j), (self.out_channel, self.x_new, self.y_new)) x_start = int(i * self.stride[0]) y_start = int(j * self.stride[1]) for ix in range(self.kernel_size[0]): for jx in range(self.kernel_size[1]): for channel_idx in range(self.in_channel): # Index for old matrix mapping_old = idx_three2one((channel_idx, x_start + ix, y_start + jx), (self.in_channel, self.x, self.y)) # We have to record, which one in the mapping matrix is from which w self.w2mapping.append([(filter_idx, channel_idx, ix, jx), (mapping_old, mapping_new)]) self.initialize = False # End Initialize input_image_flattened = X.reshape((self.n, self.old_length)) new_image_flattened = input_image_flattened.sparse_dot_with_mapping(self.w, self.w2mapping, self.old_length, self.new_length) output = new_image_flattened.reshape((self.n, self.out_channel, self.x_new, self.y_new)) # Add bias if necessary if self.bias: output1 = output + self.b return output1 return output
def forward(self, X: Variable): self.n, self.c, self.x, self.y = X.shape output = X.reshape((self.n, self.c * self.x * self.y)) return output