深度学习:卷积层的实现
文章目錄
- 卷積層的數據shape和普通層的數據shape差別:
- 卷積層實現
- 實現池化層
- 實現 CNN 中的特殊層結構
- 實現 LayerFactory
- 網絡結構
卷積層的數據shape和普通層的數據shape差別:
針對一般圖像數據shape: Npq,圖像就是二維浮點數據,N為數據個數,p,q為圖像的維度。
卷積層的中間層數據shape: Npq*r,r為channels。
數據的shape必須非常清楚,因為假如自己處理卷積層就需要用到shape
卷積層實現
1、卷積層自身多了 Kernel 這個屬性并因此帶來了諸如 Stride、Padding 等屬性,不過與此同時、卷積層之間沒有權值矩陣,
2、卷積層和普通層的shape屬性記錄的東西不同,具體而言:
普通層的shape記錄著上個 Layer 和該 Layer 所含神經元的個數
卷積層的shape記錄著上個卷積層的輸出和該卷積層的 Kernel 的信息(注意卷積層的上一層必定還是卷積層)
3、卷積填充有2種方式,tesorflow支持兩種方式:一是不填充VALID,二是全部填充SAME,沒有部分填充的方式,假如需要實現部分填充,就需要在數據預處理填充0,然后使用VALID方式卷積。
padding 可以為VALID,可以為SAME,也可以為int整型數,為int整型數時就是自填充數據。
實現池化層
對于最常見的兩種池化——極大池化和平均池化,Kernel 個數從數值上來說與輸出頻道個數一致,所以對于池化層的實現而言、我們應該直接用輸入頻道數來賦值 Kernel 數,因為池化不會改變數據的頻道數。
class ConvPoolLayer(ConvLayer):def feed_shape(self, shape):shape = (shape[0], (shape[0][0], *shape[1]))ConvLayer.feed_shape(self, shape)def activate(self, x, w, bias=None, predict=False):pool_height, pool_width = self.shape[1][1:]# 處理Paddingif self.pad_flag == "VALID" and self.padding > 0:_pad = [self.padding] * 2x = tf.pad(x, [[0, 0], _pad, _pad, [0, 0]], "CONSTANT")# 利用self._activate方法進行池化return self._activate(None)(x, ksize=[1, pool_height, pool_width, 1],strides=[1, self.stride, self.stride, 1], padding=self.pad_flag)def _activate(self, x, *args):pass# 實現極大池化 class MaxPool(ConvPoolLayer):def _activate(self, x, *args):return tf.nn.max_pool# 實現平均池化 class AvgPool(ConvPoolLayer):def _activate(self, x, *args):return tf.nn.avg_pool實現 CNN 中的特殊層結構
在 CNN 中同樣有著 Dropout 和 Normalize 這兩種特殊層結構,CNN 則通常是N×p×q×r的、其中r是當前數據的頻道數。將 CNN 中r個頻道的數據放在一起并視為 NN 中的一個神經元,這樣做的話就能通過簡易的封裝來直接利用上我們對 NN 定義的特殊層結構。
# 定義作為封裝的元類 class ConvSubLayerMeta(type):def __new__(mcs, *args, **kwargs):name, bases, attr = args[:3]conv_layer, sub_layer = basesdef __init__(self, parent, shape, *_args, **_kwargs):conv_layer.__init__(self, None, parent=parent)# 與池化層類似、特殊層輸出數據的形狀應保持與輸入數據的形狀一致self.out_h, self.out_w = parent.out_h, parent.out_wsub_layer.__init__(self, parent, shape, *_args, **_kwargs)self.shape = ((shape[0][0], self.out_h, self.out_w), shape[0])# 如果是CNN中的Normalize、則要提前初始化好γ、βif name == "ConvNorm":self.tf_gamma = tf.Variable(tf.ones(self.n_filters), name="norm_scale")self.tf_beta = tf.Variable(tf.zeros(self.n_filters), name="norm_beta")# 利用NN中的特殊層結構的相應方法獲得結果def _activate(self, x, predict):return sub_layer._activate(self, x, predict)def activate(self, x, w, bias=None, predict=False):return _activate(self, x, predict)# 將打包好的類返回for key, value in locals().items():if str(value).find("function") >= 0 or str(value).find("property"):attr[key] = valuereturn type(name, bases, attr)# 定義CNN中的Dropout,注意繼承順序 class ConvDrop(ConvLayer, Dropout, metaclass=ConvSubLayerMeta):pass# 定義CNN中的Normalize,注意繼承順序 class ConvNorm(ConvLayer, Normalize, metaclass=ConvSubLayerMeta):pass實現 LayerFactory
集合所有的layer,這樣就可以通過字符串索引到對應的layer
class LayerFactory:# 使用一個字典記錄下所有的Root Layeravailable_root_layers = {"Tanh": Tanh, "Sigmoid": Sigmoid,"ELU": ELU, "ReLU": ReLU, "Softplus": Softplus,"Identical": Identical,"CrossEntropy": CrossEntropy, "MSE": MSE,"ConvTanh": ConvTanh, "ConvSigmoid": ConvSigmoid,"ConvELU": ConvELU, "ConvReLU": ConvReLU, "ConvSoftplus": ConvSoftplus,"ConvIdentical": ConvIdentical,"MaxPool": MaxPool, "AvgPool": AvgPool}# 使用一個字典記錄下所有特殊層available_special_layers = {"Dropout": Dropout,"Normalize": Normalize,"ConvDrop": ConvDrop,"ConvNorm": ConvNorm}# 使用一個字典記錄下所有特殊層的默認參數special_layer_default_params = {"Dropout": (0.5,),"Normalize": ("Identical", 1e-8, 0.9),"ConvDrop": (0.5,),"ConvNorm": ("Identical", 1e-8, 0.9)}# 定義根據“名字”獲取(Root)Layer的方法def get_root_layer_by_name(self, name, *args, **kwargs):# 根據字典判斷輸入的名字是否是Root Layer的名字if name in self.available_root_layers:# 若是、則返回相應的Root Layerlayer = self.available_root_layers[name]return layer(*args, **kwargs)# 否則返回Nonereturn None# 定義根據“名字”獲取(任何)Layer的方法def get_layer_by_name(self, name, parent, current_dimension, *args, **kwargs):# 先看輸入的是否是Root Layer_layer = self.get_root_layer_by_name(name, *args, **kwargs)# 若是、直接返回相應的Root Layerif _layer:return _layer, None# 否則就根據父層和相應字典進行初始化后、返回相應的特殊層_current, _next = parent.shape[1], current_dimensionlayer_param = self.special_layer_default_params[name]_layer = self.available_special_layers[name]if args or kwargs:_layer = _layer(parent, (_current, _next), *args, **kwargs)else:_layer = _layer(parent, (_current, _next), *layer_param)return _layer, (_current, _next)網絡結構
class NN(ClassifierBase):def __init__(self):super(NN, self).__init__()self._layers = []self._optimizer = Noneself._current_dimension = 0self._available_metrics = {key: value for key, value in zip(["acc", "f1-score"], [NN.acc, NN.f1_score])}self.verbose = 0self._metrics, self._metric_names, self._logs = [], [], {}self._layer_factory = LayerFactory()# 定義Tensorflow中的相應變量self._tfx = self._tfy = None # 記錄每個Batch的樣本、標簽的屬性self._tf_weights, self._tf_bias = [], [] # 記錄w、b的屬性self._cost = self._y_pred = None # 記錄損失值、輸出值的屬性self._train_step = None # 記錄“參數更新步驟”的屬性self._sess = tf.Session() # 記錄Tensorflow Session的屬性# 利用Tensorflow相應函數初始化參數@staticmethoddef _get_w(shape):initial = tf.truncated_normal(shape, stddev=0.1)return tf.Variable(initial, name="w")@staticmethoddef _get_b(shape):return tf.Variable(np.zeros(shape, dtype=np.float32) + 0.1, name="b")# 做一個初始化參數的封裝,要注意兼容CNNdef _add_params(self, shape, conv_channel=None, fc_shape=None, apply_bias=True):# 如果是FC的話、就要根據鋪平后數據的形狀來初始化數據if fc_shape is not None:w_shape = (fc_shape, shape[1])b_shape = shape[1],# 如果是卷積層的話、就要定義Kernel而非權值矩陣elif conv_channel is not None:if len(shape[1]) <= 2:w_shape = shape[1][0], shape[1][1], conv_channel, conv_channelelse:w_shape = (shape[1][1], shape[1][2], conv_channel, shape[1][0])b_shape = shape[1][0],# 其余情況和普通NN無異else:w_shape = shapeb_shape = shape[1],self._tf_weights.append(self._get_w(w_shape))if apply_bias:self._tf_bias.append(self._get_b(b_shape))else:self._tf_bias.append(None)# 由于特殊層不會用到w和b、所以要定義一個生成占位符的方法def _add_param_placeholder(self):self._tf_weights.append(tf.constant([.0]))self._tf_bias.append(tf.constant([.0]))總結
以上是生活随笔為你收集整理的深度学习:卷积层的实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深度学习:dropout和BN的实现
- 下一篇: 机器学习:使用numpy实现数据增强(D