【机器学习炼丹术】的学习笔记分享
参考目录:
我们对Keras应该已经有了一个直观、宏观的认识了。现在,我们来系统的学习一下Keras的一些关于网络层的API,本文的主要内容是围绕卷积展开的,包含以下的内容:
本文内容较多,对于API的学习了解即可。
Keras的卷积层和PyTorch的卷积层,都包括1D、2D和3D的版本,1D就是一维的,2D是图像,3D是立体图像。这里就用最常见的2D图像来做讲解,1D和3D和2D基本相同,不多赘述。
先看Conv2D
的所有参数:
tf.keras.layers.Conv2D(
filters,
kernel_size,
strides=(1, 1),
padding="valid",
data_format=None,
dilation_rate=(1, 1),
groups=1,
activation=None,
use_bias=True,
kernel_initializer="glorot_uniform",
bias_initializer="zeros",
kernel_regularizer=None,
bias_regularizer=None,
activity_regularizer=None,
kernel_constraint=None,
bias_constraint=None,
**kwargs
)
先看一个简单的例子:
import tensorflow as tf
input_shape = (4, 28, 28, 3)
x = tf.random.normal(input_shape)
y = tf.keras.layers.Conv2D(
filters=2,kernel_size=3,
activation='relu',padding='same'
)
print(y(x).shape)
>>> (4, 28, 28, 2)
现在来看参数含义:
'valid'
表示没有padding,'same'
表示输出和输入特征图的尺寸相同;只有这两种选择'channels_last'
或者是'channels_first'
。默认是'channels_last'
,表示特征图的最后一个维度是通道,(batch_size, height, width, channels) ;如果选择了'channels_first'
表示每一个样本的第一个维度是通道,所以特征图的格式和PyTorch的格式相同,(batch_size, channels, height, width)。'relu'
,这个在后面的章节会详细讲解目前Keras支持的所有激活层,如果什么都不填入,则不使用激活层Keras直接提供了深度可分离卷积层,这个层其实包含两个卷积层(了解深度可分离卷积的应该都知道这个吧),一层是depthwise,一层是pointwise。
这个SeparableConv2D的参数也很多,与Conv2D有很多重复的参数,就不多加赘述了:
tf.keras.layers.SeparableConv2D(
filters,
kernel_size,
strides=(1, 1),
padding="valid",
data_format=None,
dilation_rate=(1, 1),
depth_multiplier=1,
activation=None,
use_bias=True,
depthwise_initializer="glorot_uniform",
pointwise_initializer="glorot_uniform",
bias_initializer="zeros",
depthwise_regularizer=None,
pointwise_regularizer=None,
bias_regularizer=None,
activity_regularizer=None,
depthwise_constraint=None,
pointwise_constraint=None,
bias_constraint=None,
**kwargs
)
参数详解:
但是这个深度可分离卷积完全可以用一般的Conv2D来构建,所以其实在用到深度可分离卷积的时候,自己会重新构建一个这样的网络层
对于上采样,这种方法应该并不陌生。Transposed convolution有的时候也被称为Deconvolution去卷积
tf.keras.layers.Conv2DTranspose(
filters,
kernel_size,
strides=(1, 1),
padding="valid",
output_padding=None,
data_format=None,
dilation_rate=(1, 1),
activation=None,
use_bias=True,
kernel_initializer="glorot_uniform",
bias_initializer="zeros",
kernel_regularizer=None,
bias_regularizer=None,
activity_regularizer=None,
kernel_constraint=None,
bias_constraint=None,
**kwargs
)
参数详解:
对于去卷积,可能会比较生疏,这里多讲几个例子
import tensorflow as tf
from tensorflow import keras
input_shape = (4, 28, 28, 3)
x = tf.random.normal(input_shape)
y = keras.layers.Conv2DTranspose(
filters=10,kernel_size=3,strides=1,padding='same')
print(y(x).shape)
>>> (4, 28, 28, 10)
但是假如我们去掉了padding=‘same’
input_shape = (4, 28, 28, 3)
x = tf.random.normal(input_shape)
y = keras.layers.Conv2DTranspose(
filters=10,kernel_size=3,strides=1)
print(y(x).shape)
>>> (4, 30, 30, 10)
这是因为去卷积的卷积核的中心是从原特征图的边界之外开始计算的。一个3乘3的卷积核,那么当卷积核的右下角与原特征图的左上角重合的时候,去卷积的就已经进行了一次运算,而一般的卷积是只有当卷积核的全部都与原特征图重合的时候,才进行计算的。(这里的讲解不太细致,因为之前在其他的文章中已经讲过去卷积的详细过程了)。
现在把stride改成2
input_shape = (4, 28, 28, 3)
x = tf.random.normal(input_shape)
y = keras.layers.Conv2DTranspose(
filters=10,kernel_size=3,strides=2)
print(y(x).shape)
>>> (4, 57, 57, 10)
假如加上padding='same'
input_shape = (4, 28, 28, 3)
x = tf.random.normal(input_shape)
y = keras.layers.Conv2DTranspose(
filters=10,kernel_size=3,strides=2,padding='same')
print(y(x).shape)
>>> (4, 56, 56, 10)
所以一般情况下,使用的参数是strides=2,padding='same'
,这样特征图的尺寸就刚好放大一倍。
把之前提到的简单的例子,增加卷积核和偏置的初始化:
import tensorflow as tf
input_shape = (4, 28, 28, 3)
initializer = tf.keras.initializers.RandomNormal(mean=0., stddev=1.)
x = tf.random.normal(input_shape)
y = tf.keras.layers.Conv2D(
filters=2,kernel_size=3,
activation='relu',padding='same',
kernel_initializer=initializer,
bias_initializer=initializer
)
print(y(x).shape)
>>> (4, 28, 28, 2)
简单的说,就是先定义一个初始化器initializer,然后把这个初始化器作为参数传给Keras.Layers就行了。
tf.keras.initializers.RandomNormal(mean=0.0, stddev=0.05, seed=None)
tf.keras.initializers.RandomUniform(minval=-0.05, maxval=0.05, seed=None)
tf.keras.initializers.TruncatedNormal(mean=0.0, stddev=0.05, seed=None)
基本和正态分布一样,但是如果随机的取值是在距离均值两个标准差的这个范围之外的,那么会重新取值。
换句话说,初始化的数值会被限制在均值正负两个标准差的范围内
tf.keras.initializers.Zeros()
tf.keras.initializers.Ones()
tf.keras.initializers.GlorotNormal(seed=None)
这个本质是一个截尾正态分布,但是GlorotNormal(又称Xavier),是一个以0为均值,标准差计算公式是:
是in和out表示输入和输出神经元数目的数目。如果是之前已经学习过或者看过我写的关于Xavier初始化的论文笔记的朋友,可能会发现论文中使用的是一个均匀分布而不是正态分布。
均匀分布的初始化如下:tf.keras.initializers.GlorotUniform(seed=None)
这个均匀分布是我们讲的:
这个Xavier方法,也是Keras默认的初始化的方法
当然,Keras也是支持自定义初始化的方法的。
import tensorflow as tf
class ExampleRandomNormal(tf.keras.initializers.Initializer):
def __init__(self, mean, stddev):
self.mean = mean
self.stddev = stddev
def __call__(self, shape, dtype=None)`:
return tf.random.normal(
shape, mean=self.mean, stddev=self.stddev, dtype=dtype)
def get_config(self): # To support serialization
return {'mean': self.mean, 'stddev': self.stddev}
关键就是在__call__
中返回一个和输入参数shape
大小相同的一个tf张量就行了。
基本支持了所有的常见激活函数。在卷积层的参数activation
中,可以输入relu,sigmoid,softmax等下面的字符串的形式,全部小写。
tf.keras.activations.relu(x, alpha=0.0, max_value=None, threshold=0)
tf.keras.activations.sigmoid(x)
函数方程:
tf.keras.activations.softmax(x, axis=-1)
tf.keras.activations.softplus(x)
计算公式:
tf.keras.activations.softsign(x)
计算公式:
tf.keras.activations.tanh(x)
计算公式:
tf.keras.activations.selu(x)
,返回
;
,返回
;
是事先设置的数值,alpha=1.67216214,scale=1.05070098
正则化就比较简单,不是L1就是L2,再或者两者都有。
from tensorflow.keras import layers
from tensorflow.keras import regularizers
layer = layers.Dense(
units=64,
kernel_regularizer=regularizers.l1_l2(l1=1e-5, l2=1e-4),
)
这里的正则化,可以使用:
tf.keras.regularizers.l1_l2(l1=1e-5, l2=1e-4)
tf.keras.regularizers.l2(1e-4)
tf.keras.regularizers.l1(1e-5)
关于L1和L2的计算细节:
class MyRegularizer(tf.keras.regularizers.Regularizer):
def __init__(self, strength):
self.strength = strength
def __call__(self, x):
return self.strength * tf.reduce_sum(tf.square(x))
def get_config(self):
return {'strength': self.strength}
这个实现的是L2正则的。其中的get_config是用来保存模型数据的,不要的话也没事,只是不能序列化的保存模型(不用使用config或者json来存储模型)。
- END -