CNN & Python实现(tensorflow构建一个CNN、Keras)

1.TensorFlow:开源;采用 数据流图(Data flow graphs)来计算(计算图);在会话 (Session) 的上下文 (Context) 中执行图

with tf.Session() as sess:
    result = sess.run([pro])
    print(result)

with...Device 语句用来指派特定的 CPU 或 GPU 执行操作:

with tf.Session() as sess:
    with tf.device("/cpu:0"):
        a= 1
        b= tf.constant([[2.], [2.]])
        product = tf.matmul(a, b)
        print(product)
#"/cpu:0":机器的 CPU.
#"/gpu:0":机器的第一个 GPU, 如果有的话.
#"/gpu:1":机器的第二个 GPU, 以此类推.
e_0 = tf.constant(0, name="e")
print(e_0)
e_1 = tf.constant(2, name="e")
print(e_1)
with tf.name_scope("outer"):  # 在命名空间 outer 下创建常量
    e_2 = tf.constant(2, name="e")
    print(e_2)
    with tf.name_scope("inner"):  # 在命名空间 inter 下创建常量
        e_3 = tf.constant(3, name="e")
        print(e_3)
        e_4 = tf.constant(4, name="e")
        print(e_4)
    with tf.name_scope("inner"):
        e_5 = tf.constant(5, name="e")
"""
如果 API 函数会创建新的 tf.Operation 或返回新的 tf.Tensor,
则会接受可选 name 参数。
例如,tf.constant(42.0, name="answer") 会创建一个新的 tf.Operation(名为 "answer")
并返回一个 tf.Tensor(名为 "answer:0")。如果默认图已包含名为 "answer" 的操作,
则 TensorFlow 会在名称上附加 "_1"、"_2" 等字符,以便让名称具有唯一性。
"""

在 TensorFlow 中常用的主要有四种类型的张量,分别如下:tf.Variable 变量(可变张量)、tf.constant常量、tf.placeholder占位符常量、tf.SparseTensor常量

x = tf.placeholder(tf.int32, shape=[3])  # 创建一个占位符
y = tf.square(x)  # 创建一个操作,对 x 取平方得到 y
with tf.Session() as sess:
    feed = {x: [1, 2, 3]}    # 运行时,给占位符喂的值
    print(sess.run(y, feed_dict=feed))
    print(sess.run(y, {x: [4, 5, 6]}))
mammal = tf.Variable("Elephant", tf.string)
mammal
cool_numbers = tf.Variable([3.14159, 2.71828], tf.float32)
cool_numbers
squarish_squares = tf.Variable([[4, 9], [16, 25]], tf.int32)
squarish_squares
my_image = tf.zeros([10, 299, 299, 3])
my_image

with tf.Session() as sess:
    r = tf.rank(my_image)
    r
    print(sess.run(r))
import numpy as np

data = np.random.random((1000, 32))
labels = np.random.random((1000, 10))

model.fit(data, labels, epochs=10, batch_size=32)
#使用 tf.keras.Sequential() 来构建一个简单的全连接网络
model = tf.keras.Sequential()  # 定义模型
# 添加 Dense 层,输入为 100,输出为 64 个单元,激活函数选用 Relu 函数
model.add(layers.Dense(64, input_shape=(100,), activation='relu'))
model.add(layers.Dense(64, activation='relu'))
# 添加 Dense 层,输出为 10 个单元,激活函数选用 softmax 函数
model.add(layers.Dense(10, activation='softmax'))
#model.summary()
# 创建一个层,输出单元为 64 ,激活函数为 sigmoid 函数
layers.Dense(64, activation='sigmoid')
# 创建一个层,使用 L1 正则化
layers.Dense(64, kernel_regularizer=tf.keras.regularizers.l1(0.01))
# 创建一个层,使用 L2 正则化
layers.Dense(64, bias_regularizer=tf.keras.regularizers.l2(0.01))
# 创建一个层,偏置参数的初始值设置为 2
layers.Dense(64, bias_initializer=tf.keras.initializers.constant(2.0))
model = tf.keras.Sequential([
    # 添加一个 Dense 层,输出为 64 ,激活函数为 Rule
    layers.Dense(64, activation='relu'),
    layers.Dense(64, activation='relu'),
    # 添加一个 Dense 层,输出为 64 ,激活函数为 softmax
    layers.Dense(10, activation='softmax')])
model.compile(optimizer=tf.train.AdamOptimizer(0.001),
              loss='categorical_crossentropy',
              metrics=['accuracy'])
model.compile(optimizer=tf.train.AdamOptimizer(0.01),  # 使用 Adam 优化算法
              loss='mse',       # 损失函数选用均方差损失
              metrics=['mae'])  # 使用均方差来评估模型

2.Keras 是一个基于 Python 的高层神经网络库,其可以使用 Tensorflow、Theano 以及 CNTK 等著名深度学习框架作为后端

计算机视觉领域,有 3 个最著名的比赛,分别是:ImageNet ILSVRC,PASCAL VOC 和微软 COCO 图像识别大赛

目前,Keras 包含有 5 个预训练模型,分别为:Xception,VGG16,VGG19,ResNet50,InceptionV3,MobileNet

通过预训练模型实现迁移学习:

3.构建神经网络:机器通过大量的数据学习得到一个函数去模拟真实数据

激励函数:例如一个神经元对猫眼睛敏感,那当它看到猫眼睛的时候就被激励了,相应的参数就会被调优,它的贡献就会越大。

激励函数在预测层,判断哪些值要被送到预测结果那里。例如RELU等的改进。

搭建神经网络首先是处理训练数据。

import numpy as np

x_data = np.linspace(-1, 1, 300)[:, np.newaxis]
noise = np.random.normal(0, 0.05, x_data.shape)
y_data = np.square(x_data) - 0.5 + noise

定义节点准备接收数据:

xs = tf.placeholder(tf.float32, [None, 1])
ys = tf.placeholder(tf.float32, [None, 1])

接下来,需要定义我们的神经层,即添加隐藏层和输出层。

这里我们先定义一个函数,输入参数有 inputsin_sizeout_size, 和 activation_function

def add_layer(inputs, in_size, out_size, activation_function=None):
    # 定义参数 Weights,biases
    Weights = tf.Variable(tf.random_normal([in_size, out_size]))
    biases = tf.Variable(tf.zeros([1, out_size]) + 0.1)
    # 定义拟合公式
    Wx_plus_b = tf.matmul(inputs, Weights) + biases
    # 激励函数
    if activation_function is None:
        outputs = Wx_plus_b
    else:
        outputs = activation_function(Wx_plus_b)
    return outputs

使用 add_layer(inputs, in_size, out_size, activation_function=None) 添加层:

# 添加隐藏层(hidden layer) 输入值是 xs,在隐藏层有 10 个神经元,使用 ReLu 激活函数
# 输入 1 维数据,经隐藏层处理输出 10 维数据

l1 = add_layer(xs, 1, 10, activation_function=tf.nn.relu)

# 添加输出层(output layer) 输入值是隐藏层 l1,在预测层输出 1 个结果
# 从隐藏层输入得到的 10 维数据,经输出层处理 输出 1 维数据

prediction = add_layer(l1, 10, 1, activation_function=None)

loss 表达式(预测结果与真实结果的误差):

loss = tf.reduce_mean(tf.reduce_sum(
    tf.square(ys - prediction), reduction_indices=[1]))

选择 optimizer 使 loss 达到最小(定义一种方法减少 loss):

# 最基本的 Optimizer:Gradient Descent,学习率=0.1
optimizer = tf.train.GradientDescentOptimizer(0.1)
train_step = optimizer.minimize(loss)

我们知道 TensorFlow 在对变量完成创建之后,还需要对变量初始化:

init = tf.global_variables_initializer()

在 Session 中启动计算图:

sess = tf.Session()
# 上面操作在 TensorFlow 中都没有产生运算,直到 sess.run 才会开始运算
sess.run(init)

设置迭代次数:

# 迭代 1000 次学习,sess.run optimizer
for i in range(1000):
    # training train_step 和 loss 都是由 placeholder 定义的运算,所以这里要用 feed 传入参数, placeholder 和 feed_dict 是绑定用的。
    sess.run(train_step, feed_dict={xs: x_data, ys: y_data})
    if i % 50 == 0:
        # 观察每一步的变化
        print(sess.run(loss, feed_dict={xs: x_data, ys: y_data}))

改进神经网络

过拟合是很多机器学习的通病,Dropout 的出现很好的可以解决这个问题。Dropout 是指在深度学习网络的训练过程中,对于神经网络单元,按照一定的概率将其暂时从网络中丢弃。注意是暂时,对于随机梯度下降来说,由于是随机丢弃,故而每一个 mini-batch 都在训练不同的网络。Dropout 是卷积神经网络中防止过拟合提高效果的一个大杀器。

代码实现就是在 add layer 函数里加上 dropoutkeep_prob 就是保持多少不被 Drop,在迭代时在 sess.run 中被 feed:

def add_layer(inputs, in_size, out_size, activation_function=None):
    # 定义参数 Weights,biases
    Weights = tf.Variable(tf.random_normal([in_size, out_size]))
    biases = tf.Variable(tf.zeros([1, out_size]) + 0.1)
    # 定义拟合公式
    Wx_plus_b = tf.matmul(inputs, Weights) + biases
    # 隐含节点 dropout 率等于 0.5 的时候效果最好,即 keep_prob=0.5,
    # 原因是 0.5 的时候 dropout 随机生成的网络结构最多。
    Wx_plus_b = tf.nn.dropout(Wx_plus_b, keep_prob=0.5)
    # 激励函数
    if activation_function is None:
        outputs = Wx_plus_b
    else:
        outputs = activation_function(Wx_plus_b)
    return outputs

构建卷积神经网络

有了神经网络,我们就可以对输入的信息进行特征提取,但是图像包含的信息量巨大,一般的神经网络并不能准确的提取图像的特征,这时 卷积神经网络(Convolutional Neural Networks, CNN) 就是计算机处理图像的助推器,有了它,计算机理解图像就会更准确。卷积神经网络包含输入层、隐藏层和输出层,隐藏层又包含 卷积层(Conv) 和 池化层(Pooling)。

下图展示了,卷积层处理图像的基本过程:

从图上我们可以看出,立方体不断的增加厚度,这是因为图像输入到卷积神经网络后通过 卷积核 来不断的提取特征,每提取一个特征就会增加一个 Feature Map。那么为什么厚度增加了但是却越来越瘦了呢,这是因为 Pooling 层 的作用,Pooling 层本质是下采样,通常采用的是最大值 Pooling 和平均值 Pooling,因为参数太多会导致计算越来越复杂,所以通过 Pooling 来稀疏参数,使我们的网络不至于太复杂。

现在我们对卷积神经网络已经有了大概的了解,下节我们将通过代码来实现一个简单的卷积神经网络完成手写数字识别,搭建的卷积神经网络结构如下图所示:

首先是数据预处理过程,我们使用 scikit-learn 提供的数据集,并完成形状处理。

from sklearn.datasets import load_digits  # sklearn 为我们提供的手写数字数据集

# 数据预处理
digits = load_digits()
X_data = digits.data.astype(np.float32)
y_data = digits.target.astype(np.float32).reshape(-1, 1)

X_data.shape, y_data.shape

接下来,对数据进行标准化处理,可以提升模型的训练效果。

# 数据的标准化(normalization)是将数据按比例缩放,
# 使之落入一个小的特定区间。这样去除数据的单位限制,
# 将其转化为无量纲的纯数值,便于不同单位或量级的指标能够进行比较和加权。
from sklearn.preprocessing import OneHotEncoder
from sklearn.preprocessing import MinMaxScaler
scaler = MinMaxScaler()
X = scaler.fit_transform(X_data)
y = OneHotEncoder(categories='auto').fit_transform(
    y_data).todense()  # one-hot 编码

X.shape, y.shape

这里,我们提前定义一个 generatebatch 函数,以帮助后续实现对数据小批量处理。

def generatebatch(X, y, n_examples, batch_size):
    for batch_i in range(n_examples // batch_size):
        start = batch_i*batch_size
        end = start + batch_size
        batch_xs = X[start:end]
        batch_ys = y[start:end]
        yield batch_xs, batch_ys  # 生成每一个 batch

接下来,我们实现最重要的网络结构部分。

tf_X = tf.placeholder(tf.float32, [None, 8, 8, 1])
tf_y = tf.placeholder(tf.float32, [None, 10])

conv_filter_w1 = tf.Variable(tf.random_normal([3, 3, 1, 10]))
conv_filter_b1 = tf.Variable(tf.random_normal([10]))
relu_feature_maps1 = tf.nn.relu(
    tf.nn.conv2d(tf_X, conv_filter_w1, strides=[1, 1, 1, 1], padding='SAME') + conv_filter_b1)
relu_feature_maps1

参数说明:

  • data_format :表示输入的格式,有两种分别为:NHWC 和 NCHW,默认为 NHWC。
  • input :输入是一个 4 维格式的(图像)数据,数据的 shape 由 data_format 决定:当 data_format 为 NHWC 输入数据的 shape 表示为 [batch, in_height, in_width, in_channels],分别表示训练时一个 batch 的图片数量、图片高度、 图片宽度、 图像通道数。当 data_format 为 NHWC 输入数据的 shape 表示为 [batch, in_channels, in_height, in_width] 。
  • filter :卷积核是一个 4 维格式的数据:shape 表示为:[height,width,in_channels, out_channels],分别表示卷积核的高、宽、深度(与输入的 in_channels 应相同)、输出 feature map 的个数(即卷积核的个数)。
  • strides :表示步长:一个长度为 4 的一维列表,每个元素跟 data_format 互相对应,表示在 data_format 每一维上的移动步长。当输入的默认格式为:NHWC,则 strides = [batch , in_height , in_width, in_channels]。其中 batch 和 in_channels 要求一定为 1,即只能在一个样本的一个通道上的特征图上进行移动,in_height , in_width 表示卷积核在特征图的高度和宽度上移动的布长,即 strideheight 和 stridewidth 。
  • padding :表示填充方式:SAME 表示采用填充的方式,简单地理解为以 0 填充边缘,当 stride 为 1 时,输入和输出的维度相同;VALID表示采用不填充的方式,多余地进行丢弃。

池化层:

max_pool1 = tf.nn.max_pool(relu_feature_maps1, ksize=[1, 3, 3, 1], strides=[
                           1, 2, 2, 1], padding='SAME')

# - value:表示池化的输入:一个 4 维格式的数据,数据的 shape 由 data_format 决定,
# 默认情况下 shape 为[batch, height, width, channels]
# 其他参数与 tf.nn.cov2d 类型
# - ksize:表示池化窗口的大小:一个长度为 4 的一维列表,一般为[1, height, width, 1],
# 因不想在 batch 和 channels 上做池化,则将其值设为 1。

max_pool1

卷积层:

conv_filter_w2 = tf.Variable(tf.random_normal([3, 3, 10, 5]))
conv_filter_b2 = tf.Variable(tf.random_normal([5]))
conv_out2 = tf.nn.conv2d(max_pool1, conv_filter_w2, strides=[
                         1, 2, 2, 1], padding='SAME') + conv_filter_b2
conv_out2

BN 归一化层 + 激活层:

batch_mean, batch_var = tf.nn.moments(conv_out2, [0, 1, 2], keep_dims=True)
shift = tf.Variable(tf.zeros([5]))
scale = tf.Variable(tf.ones([5]))
epsilon = 1e-3
BN_out = tf.nn.batch_normalization(
    conv_out2, batch_mean, batch_var, shift, scale, epsilon)
relu_BN_maps2 = tf.nn.relu(BN_out)
relu_BN_maps2

参数说明:

  • mean 和 variance 通过 tf.nn.moments 来进行计算。
  • batch_mean, batch_var = tf.nn.moments(x, axes = [0, 1, 2], keep_dims=True),注意 axes 的输入。对于以 feature map 为维度的全局归一化,若 feature map 的 shape 为 [batch, height, width, depth],则将 axes 赋值为 [0, 1, 2] 。
  • x 为输入的 feature map 四维数据,offset、scale 为一维 Tensor 数据,shape 等于 feature map 的深度 depth。

池化层:

max_pool2 = tf.nn.max_pool(relu_BN_maps2, ksize=[1, 3, 3, 1], strides=[
                           1, 2, 2, 1], padding='SAME')
max_pool2

将特征图进行展开:

max_pool2_flat = tf.reshape(max_pool2, [-1, 1*1*5])
max_pool2_flat

全连接层:

fc_w1 = tf.Variable(tf.random_normal([1*1*5, 50]))
fc_b1 = tf.Variable(tf.random_normal([50]))
fc_out1 = tf.nn.relu(tf.matmul(max_pool2_flat, fc_w1) + fc_b1)
fc_out1

输出层:

out_w1 = tf.Variable(tf.random_normal([50, 10]))
out_b1 = tf.Variable(tf.random_normal([10]))
pred = tf.nn.softmax(tf.matmul(fc_out1, out_w1)+out_b1)
pred

开始训练:

loss = -tf.reduce_mean(tf_y*tf.log(tf.clip_by_value(pred, 1e-11, 1.0)))
# Adam 优化算法:是一个寻找全局最优点的优化算法,引入了二次方梯度校正。
# 相比于基础 SGD 算法,1.不容易陷于局部优点。2.速度更快
train_step = tf.train.AdamOptimizer(1e-3).minimize(loss)

y_pred = tf.math.argmax(pred, 1)
accuracy = tf.reduce_mean(
    tf.cast(tf.equal(tf.math.argmax(tf_y, 1), y_pred), tf.float32))  # 准确率
accuracy
batch_size = 32  # 使用 MBGD 算法,设定 batch_size 为 32
epochs = 50  # 迭代 50 个周期
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    for epoch in range(epochs):
        # 每个周期进行 MBGD 算法
        # 转换为图片的格式 (batch,height,width,channels)
        X = X.reshape(-1, 8, 8, 1)
        for batch_xs, batch_ys in generatebatch(X, y, y.shape[0], batch_size):
            sess.run(train_step, feed_dict={tf_X: batch_xs, tf_y: batch_ys})
        res = sess.run(accuracy, feed_dict={tf_X: X, tf_y: y})
        print('Epoch [{}/{}], Accuracy: {:.1f}%'
              .format(epoch+1, epochs, res*100))
 
发布了40 篇原创文章 · 获赞 3 · 访问量 7590

猜你喜欢

转载自blog.csdn.net/OpenSceneGraph/article/details/101157170
CNN
今日推荐