开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第4天,[点击查看活动详情]
前言
上一次,我们了解了线性回归模型,并对深度学习模型构建有了一个基本的认识
这一次,我们将试着分析一下分类问题,就以经典的手写数字识别为例 ( 基于MNIST数据集 )
手写数字识别
我们知道图像事实上就是一个个像素构成,如果是彩色图像,又有RGB三个颜色通道,对于手写数字识别的问题来说,我们不关心其颜色,因此,我们使用灰度图即可,灰度图则可以用二维的灰度值矩阵表示
构建模型
我们这次仍然先尝试用线性回归模型的思路构建模型,首先遇到的第一个问题就是此前我们的线性回归模型的权重与偏置是一个向量,输入输出也是向量
然而,这一次,我们的输入是一个矩阵,我们会处理的是向量,因此,我们可以将矩阵展平成1维的向量,
下图很好地表示了这一个过程
输入问题解决了,输出又该怎么办?某图片是某一类这个表述似乎难以量化,我们需要借助一种思路--独热编码(One-hot)
独热编码:我们可以构建一个与类别维度相等的向量,如果某个东西属于某一类,我们可以将对应维度记为1,其余维度记为0,用这样构建的一个向量表示这个东西属于这一类
比如图片有十类,第一张图片属于第2类,那么它的独热编码就可以写成
至此输入输出问题就解决了,接下来,就是损失函数的问题了,我们仍然用MSE吗?
MSE或许确实不是最为合适的了,然而,我们目前也没有更好的策略,就仍然使用MSE
新的问题随之而来,对于线性模型,我们的操作无非就是对向量做线性变换,然而,线性关系毕竟是太特殊了,某个输出与输入的关系稍复杂一些线性模型就表现很差了,因此,我们必须提高模型的非线性特性
激活函数
我们给出的对策是在输入层与全连接层之前加入隐藏层,使用非线性的激活函数,增强模型的非线性特征,提高模型的表达能力
我们介绍一种常用的激活函数---RELU激活函数:
非线性输出就可以表示成:
带有隐藏层的神经网络模型如下如所示:
代码实现
说了很久理论了,不实践一下怎么行?请看代码:
%matplotlib inline
import os
import matplotlib.pyplot as plt
import tensorflow as tf
import tensorflow.keras.datasets as datasets
plt.rcParams['font.size']=16
plt.rcParams['font.family']=['STKaiti']
plt.rcParams['axes.unicode_minus']=False
def load_data():
(x,y),(x_val,y_val)=datasets.mnist.load_data() #导入数据集
#转为张量并且归一化
x=tf.convert_to_tensor(x,dtype=tf.float32)/255.-1
y=tf.convert_to_tensor(y,dtype=tf.int32)
#转为独热编码
y=tf.one_hot(y,depth=10)
#展平
x=tf.reshape(x,(-1,28*28))
#构建训练集对象
train_dataset=tf.data.Dataset.from_tensor_slices((x,y))
#分批量
train_dataset=train_dataset.batch(200)
return train_dataset
def init_parameters():
#第一层参数初始化
w1=tf.Variable(tf.random.truncated_normal([784,256],stddev=0.1))
b1=tf.Variable(tf.zeros([256]))
#第二层
w2=tf.Variable(tf.random.truncated_normal([256,128],stddev=0.1))
b2=tf.Variable(tf.zeros([128]))
#第三层
w3=tf.Variable(tf.random.truncated_normal([128,10],stddev=0.1))
b3=tf.Variable(tf.zeros([10]))
return w1,b1,w2,b2,w3,b3
def train_epoch(epoch,train_dataset,w1,b1,w2,b2,w3,b3,lr=0.001):
for step,(x,y) in enumerate(train_dataset):
with tf.GradientTape() as tape: #梯度记录的语法糖
h1=x@w1+tf.broadcast_to(b1,(x.shape[0],256))
h1=tf.nn.relu(h1)
h2=h1@w2+b2
h2=tf.nn.relu(h2)
out=h2@w3+b3
loss=tf.square(y-out)
loss=tf.reduce_mean(loss)
grads=tape.gradient(loss,[w1,b1,w2,b2,w3,b3])
#更新参数
w1.assign_sub(lr*grads[0])
b1.assign_sub(lr*grads[1])
w2.assign_sub(lr*grads[2])
b2.assign_sub(lr*grads[3])
w3.assign_sub(lr*grads[4])
b3.assign_sub(lr*grads[5])
if step%100==0:
print(epoch,step,'loss:',loss.numpy())
return loss.numpy()
def train(epochs):
losses=[]
train_dataset=load_data()
w1,b1,w2,b2,w3,b3=init_parameters()
for epoch in range(epochs):
loss=train_epoch(epoch,train_dataset,w1,b1,w2,b2,w3,b3,lr=0.01)
losses.append(loss)
x=[i for i in range(0,epochs)]
#画图
plt.plot(x,losses,color='blue',marker='s',label='训练',markersize='3')
plt.xlabel('Epoch')
plt.ylabel('MSE')
plt.legend()
plt.show()
plt.close()
if __name__=='__main__':
train(50)
下图是训练效果: