Char3-分类问题
在人工智能上花一年时间,这足以让人相信上帝的存在————艾伦cdot佩利
分类问题典型的应用就是教会机器如何去自动识别图片中物体的种类。本章中主要是介绍了MNIST数据集。
手写数字数据集介绍
数据集是手写数字0-9,具有的特征为:
- 真人书写的0-9数字图片
- 为了便于存储和计算,将图片缩放到固定的大小size,比如224224,或者9696:作为输入x
- 每张图片加上标签
label
,作为图片的真实值y
- 一般是通过映射方式进行类别命名;通过
0-1
表示正反面的方式,叫做数字编码。
MNIST数据集具体信息
特征
- 包含0-9共10中数字的手写图片,每种数字7000张,总共70000张
- 60000张是属于训练集,剩下的属于测试集
- 每张图片缩放到28*28的大小
- 图片是真人书写,包含了:字体大小、书写风格、粗细等丰富的样式
每张图片信息
- 每张图片包含h行(height、row)和w(width、column)列
- 每个位置保存了pixel像素值,像素值使用的是0-255来表示强度值,0表示最低,255表示最高
- 如果是彩色图片,每个图片包含RGB三通道,分别是红色、绿色、黄色的颜色强度。保存的形状是[h,w,3]的张量Tensor,即3维数组
- 如果是灰色照片,像素点就是一个1维、长度为3的向量;使用形状为[h,w]的二维数组来表示一张图片信息,也可以表示成[h,w,1]形状的张量
利用TF下载MNIST数据
- 导入各种子库
- 加载数据集
- 将数据转成张量形式
- 将输出y转换成
one-hot
编码形式 - 构建数据集对象和批量训练
import os
import tensorflow as
from tensorflow import kreas # 导入子库
from tensorflow.kreas import layers, optimizers, datasets
(x,y), (x_val, y_val) = datasets.mnist.load_data() # 加载数据集
x = 2 * tf.convert_to_tensor(x, dtype=tf.float32) / 255.-1 # 转成张量,并且缩放到-1到1之间
y = tf.convert_to_tensor(y, dtype=tf.int32) # 转成张量
y = tf.one_hot(y, depth=10) # 热编码形式
print(x.shape, y_shape)
train_dataset = tf.data.Dataset.from_tensor_slices(x,y) # 构建数据集对象
train_dataset = train_data.batch(512) # 批量训练
代码的具体解释:
- load_data()函数:返回的是两个元组对象:训练集 测试集;每个元组的第一个元素是多个训练图片数据训练集数据X,第二个元素是训练图片对应的类别数字Y
- 训练集X大小是(60000,28,28),灰色照片,没有RGB通道
- 训练集Y大小是(60000),代表的是标签,每个标签用一个0-9的数字表示
- 测试集X的大小是(10000,28,28),10000张测试图片,Y的大小是(10000,),也是标签
图片表示方法
- 一张图片用shape为[h,w]的矩阵来表示;
- 多张图片前面加上维度dimension,使用shape为[b,h,w]的张量来表示,其中b表示batch size(批量)
- 多张彩色图片使用shape为[b,h,w,c]表示,c表示的是通道数量channel,彩色图片c=3
调用batch()函数即可构建带batch功能的数据集对象
模型构建
向量形式的生成
- 回归模型中,一组长度为d_{in}的输入向量x=[x_1,x_2,…,x_n]^T简化为x,表达式为y=x*w b
- 多输入、单输出的模型结构,借助向量形式
- 多输出节点、批量训练方式,模型写成张量形式
其中Xin R^{b times d_{in}},b in R^{d_{out}},Yin R^{b times d_{out}},Win R^{b_{in} times d_{out }}
- d_{in}表示输入节点数目
- d_{out}表示输出节点数目
- X的形状shape为[b,d_{in}],表示b个样本的输入,每个样本的特征长度d_{in}
- W的shape为[d_{in},d_{out}],包含d_{in}*d_{out}个网络参数
- 偏置向量b的shape为d_{out},每个输出节点上允许添加一个偏置值
- @符号表示的是矩阵相乘
对应模型为:
x_1^1中的上标表示样本索引号(表示第几个样本),下标表示样本向量的元素(样本的第几个特征)
图片识别任务转成张量形式
- 图片的输入格式
一张图片使用的是矩阵方式存储,shape为:[h,w];
b张图片使用shape为[b,h,w]的张量X进行存储
模型只接受向量形式的输入特征向量,需要将矩阵形式平铺成[hw]的向量,输入特征的长度为d_{in}=hw
- 对于输出标签
数字编码的结果(比如1表示猫,2表示鱼,3表示狗)之间存在天然的大小关系。
解决方法
- 将输出设置为d_{out}个输出节点的向量,d_{out}与类别数相同
- 让第i in [1,d_{out}]个输出值表示当前样本属于类别i的概率P
- 如果属于第i类,索引为i的位置设置为1,其余为0!!!!
- 下图中:对于所有猫的图片,数字编码是0,one-hot编码为[1,0,0,0];其他类推
- 手写数字图片数据
总类别数是10,即输出节点总数值d_{out}=10,假设某个样本的类别是i,即图片中的数字是i,需要一个长度为10的向量y,索引号为i的位置设置为1,其余是0。
- 0的one-hot编码是[1,0,0,0,….]
- 1的one-hot编码是[0,1,0,0,….]
- 其余类推
One-hot编码是非常稀疏Sparse
的,占用的存储空间多,所以在存储的时候还是采用数字编码。
# 数字编码转成 one-hot 编码
y = tf.constant([0,1,2,3]) # 数字编码
y = tf.ont_hot(y, depth=10) # one-hot编码
关于手写数字图片:
- 输入是一张打平后的图像量x in R^{28*28}
- 输出是长度为10的向量o in R^{10}
- 真实标签y经过one-hot变成长度为10的稀疏向量yin {0,1}^{10}
- 多输入和多输出的线性预测模型是o=W^Tx b,希望其更接近真实标签y
误差计算
对于分类问题,目标是优化某个性能指标,比如准确度acc。常用的做法是设立一个平滑可导的代理目标函数:模型输出o和真实标签y之间的距离。
通过优化损失函数来找到最优解,采用的是交叉熵cross entropy损失函数
1. 存在的问题:
- 欠拟合(左图):线性模型,表达能力差
- 过拟合(右图):模型过于复杂,表达能力过强,伤害模型的泛化能力
2. 非线性模型
代码语言:javascript复制layers.Dense(256, activation='relu') # 长度为256的向量
3. 多层神经网络
- 将前一层神经元的输出值作为下一 层的输入值
- 将最后一层的输出值作为模型的输出值
- 几个比较基础的概念 输入层:数据节点所在的层 网络层:输出h_i连同它的网络层参数w,b 隐藏层:网络层中间的层 输出层:最后一层
体验手写数字识别
网络搭建
代码语言:javascript复制# 构建3层网络
model = keras.Sequential([
layers.Dense(256, activation='relu'), # 第1层的输出节点数为256
layers.Dense(128, activation='relu'),
layers.Dense(10) # 输出层节点数设计为10
])
模型训练
对数据集中的所有图片迭代一遍叫做一个Epoch
with tf.GradientTape() as tape: # 构建梯度记录环境
# 1. 拍平 [b, 28, 28] ---> [b, 784]
x = tf.reshape(x, (-1, 28*28))
# 2. 输出 [b,784] --->[b,10]
out = model(x)
# 3. 计算梯度:自带的自动求导函数求解
grads = tape.gradient(loss, model.trainable_variables)
# 4. 更新网络参数
optimizer.apply_gradients = (zip(grads, model.trainable_variables))