深度学习_03_分类问题_手写数字问题&体验
分类问题Discrete Prediction(离散值的预测)[up,left,down,right]、[dog,cat,whale,…]ImageNet:包含1000种常见生活物体Hand-written Digits Recignition(手写数字体识别)MNIST数据集Image[28,28,1] 28行28列 -> [784]每个像素点为0~255(灰度值)彩色图片的话可能为[ 2
分类问题
Discrete Prediction(离散值的预测)
- [up,left,down,right]、[dog,cat,whale,…]
ImageNet:包含1000种常见生活物体
Hand-written Digits Recignition(手写数字体识别)
MNIST数据集
Image
[28,28,1] 28行28列 -> [784]
每个像素点为0~255(灰度值)
彩色图片的话可能为[ 28 , 28 , 3(RGB)]
为了获得打平后的向量的值,变成一维的数值。后一行的数值放在前一行的末尾。[28,28] -> [28*28] = [784]
Input and Output
分类问题的输出。输入是[b,784],b理解为一共有多少张图片。
分类问题的分类
最简单的方法是 给每一个类别直接编号一个具体的数值
-
dog=0,cat=1,fish=2…
1.1理解为cat,2.2理解为fish
这种编码方式非常直观好理解,但是有两个致命问题
- 数字之间有天生的大小关系,而类别是独立的存在
- 我们会觉得2.2比2.3更符合2,而不是3。但是这样的解释并不符合概率论的知识概念
更好的方式是,把每种具体的类别理解为具体的node,9个类别有9个node,没有大小(顺序)的关系。还有第二,每一个node有具体的实数值,这个值会归一化为0~1的区间。
每个node为 P(y=0|x)…
并且会尽量使它们之间的和为1。因为每张图片是0~9的其中1个,它总是会属于这10个node中的一个节点,这样会使得 ∑ i = 0 − 9 P ( y = i ∣ x ) = 1 \sum_{i=0-9} P(y=i|x) = 1 ∑i=0−9P(y=i∣x)=1。这是一种输出的编码方式,这种方式叫one-hot编码,因为每一张图片只属于输出节点中的一种。
10个类别的概率和为1,它们之间有一个最大的概率,把它理解为当前类别的置信度。去最大的概率的node的名字作为当前物体的识别种类的归类
Regression VS Classification
套用Regression的机制来直接处理Classification的问题
对于Regression,给出了w和b参数以后,生成了y的预测值,y属于连续的范围, R d R^d Rd(一维的线性的实数空间),不一定覆盖全部实数空间,比如温度。
对于Classification,out输出为1维的长度为10的向量,每一个元素所在的位置就是当前 P ( y = i ∣ x ) P(y=i|x) P(y=i∣x),每一个值属于0~1的范围。我们把输出值所在位置最大的那个值概率叫做置信度,值所在的索引,在上图也就是1把它理解为当前图片数据1号元素的概率P(y=1|x) = 0.8,理解为这张图片可能就是1了。
Computation Graph
具体的Classification计算。
输入是28*28,打平成784 -》[1,784],一张照片有784个像素
W和b根据相应的维度来匹配,W[784,10] b[10]。《-因为输出肯定是[1,10]的矩阵,10类的置信度,反推W和b的形状。要满足相乘规则只能W[784,10]。
[1,10] + [10]是可以直接进行运算的,经过broadencast -》 [1,10],理解为【0.1(label=0),0.8(label=1),…】,理解为属于1的概率最大
但是现在不算是完成了Classification的求解
第一个问题是这个模型是线性的。
对于回归问题,大部分情况都比较简单,可以用Linear模型去完成Regression问题,所以Linear Regression采用的比较多;但是对于一个高维的图片的识别,一张图片识别的逻辑是非常复杂的,光线性模型是不能完成手写图片识别的复杂任务,它有784维的输入。
因此我们需要添加进来一个非线性的因子,在原来的输出上面添加f function,它必须是非线性的,这样才能引入非线性因子在里面,我们也不希望太复杂,把f function叫做激活函数(activation),有一种非常常见的形式,叫做ReLU。ReLU是非线性的,是一条折线。使用激活函数,使得现在的整个模型就已经是非线性模型了。
第二个问题是太简单了。
光光一次输出还是难以满足识别。我们想到火车的模型,我们将第一次的输出当做下一个层的输入,称为隐藏层。一层层地连接。
Particularly
给一个输入X[1,784],进行一次计算相当于降维的过程,重复降维过程,最终降到[1,10],恰好是我们想要输出的形式,不停地挂接工序,随后转义为置信度。
[1,784] -> [1,512] -> [1,256] -> [1,10]
结果 [0,0,0.1,0.1,0.8, …]
Loss?
label: 0~9,out和label的维度都是[1,10]。对于三维为例,Label在坐标轴上,而out(输出值)在空间上,我们期望out越来越逼近于y,因为我们希望输出值接近于真实值。计算loss,我们可以计算MSE(欧氏距离),也就是out到y之间的欧氏距离, l o s s = ∑ ( y − o u t ) 2 loss = \sum(y-out)^2 loss=∑(y−out)2。
经过三道工序,得到输出。
loss函数计算是out和label之间做MSE(欧氏距离),用loss来优化参数,也就是 [ W 1 ′ , b 1 ′ , W 2 ′ , b 2 ′ , W 3 ′ , b 3 ′ ] [W_1',b_1',W_2',b_2',W_3',b_3'] [W1′,b1′,W2′,b2′,W3′,b3′],不停地最小化它们。
然后用这些参数来预计最新的X,获得新的out,取out概率最大的那个元素的index,它就可能属于类别index。
深度学习。三大工序的串接已经有deep learning的模型了,只不过层数更深。
Classification Procedure
Next下一步
- MNIST感受学习
- 学习TensorFlow
- 自己实现第1步的过程
手写数字问题初体验
对于MNIST,输入的节点数为0,1,2…783。
输出为0,1,2,…9
整个计算流程包含了5步:
- 数据集的准备
- 完成前面的推算,h1,h2,out。
- 根据实际的y计算loss
- 根据loss求解梯度并且更新网络中间的参数,[w1,b1,w2,b2,w3,b3]
- 根据数据集进行循环往复的更新,随后停止。获得[w1,b1,w2,b2,w3,b3]后就可以进行新的样本的预测了。
实际代码
import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import datasets,layers,optimizers
#自动下载或调用mnist的图片,training-60K,test-10K
(x,y),_ = datasets.mnist.load_data()
#60张图片,28*28-(60000, 28, 28)
# 每个y的label为0~9的数字这里还没有进行one-hot编码(存储不高效),运算时在onehot-(60000,)。
#在这里为1维的,长度为60K的数字
print('datasets:',x.shape,y.shape)
#上面x数据返回的是numpy的格式,需要转换为TensorFlow支持的载体格式-tensor
x = tf.convert_to_tensor(x,dtype=tf.float32) / 255
y = tf.convert_to_tensor(y, dtype=tf.int32)
#进行one hot编码
y = tf.one_hot(y, depth=10)
#(60000, 28, 28) (60000, 10)
print(x.shape, y.shape)
#转换成Dataset类型,做一次运算,比如一个样本[1,784],对GPU来说很快,
#因此可以完成多个样本,这里有b。[b,784],有batch概念。做做运算不是针对1个图,而是多个图片,高度并行化流程
train_dataset = tf.data.Dataset.from_tensor_slices((x,y))
#一次加载200张图片
train_dataset = train_dataset.batch(200)
# for step,(x,y) in enumerate(db):
# print(step,x.shape,y.shape)
#准备网络结构和优化器
model = keras.Sequential([
layers.Dense(512,activation='relu'),
layers.Dense(256,activation='relu'),
layers.Dense(10)])
#w' = w - lr*(\del loss/del w)
#代码实现的是自动更新参数
optimizer = optimizers.SGD(learning_rate=0.01)
#一个数据集迭代一次,一次数据集完成叫做一次epoch
def train_epoch(epoch):
# Step4.loop
#重复更新参数的循环
#step=batch,一个batch迭代一次,一共重复300次若batch=200的话,300*200=60K
for step, (x, y) in enumerate(train_dataset):
with tf.GradientTape() as tape:
# [b, 28, 28] => [b, 784]
x = tf.reshape(x, (-1, 28 * 28))
# Step1. compute output
#h1 h2 out
# [b, 784] => [b, 10]
out = model(x)
# Step2. compute loss
#(1/N) \sum (y-out)^2
loss = tf.reduce_sum(tf.square(out - y)) / x.shape[0]
# Step3. optimize and update w1, w2, w3, b1, b2, b3
#传统先求\del loss / \del w,h1->h2->out,知道它们可导,但是这样非常复杂和麻烦
#下面自动求导,loss,model.trainable_variables=[w1, w2, w3, b1, b2, b3]
#返回梯度,第一个\del loss / \del w1 ...
grads = tape.gradient(loss, model.trainable_variables)
# w' = w - lr * grad
#分别更新参数[w1, w2, w3, b1, b2, b3],利用上式,w(或b)为以前的w(或b),grad为算出来的导数
optimizer.apply_gradients(zip(grads, model.trainable_variables))
if step % 100 == 0:
print(epoch, step, 'loss:', loss.numpy())
def train():
#对数据集迭代30次,30个epoch
for epoch in range(30):
train_epoch(epoch)
if __name__ == '__main__':
train()
一部分结果
datasets: (60000, 28, 28) (60000,)
(60000, 28, 28) (60000, 10)
0 0 loss: 1.8705982
0 100 loss: 0.5503684
0 200 loss: 0.39639312
1 0 loss: 0.3495443
1 100 loss: 0.3756662
1 200 loss: 0.29594418
2 0 loss: 0.2724363
2 100 loss: 0.3165485
2 200 loss: 0.2512417
3 0 loss: 0.23431014
3 100 loss: 0.28171945
3 200 loss: 0.22504988
4 0 loss: 0.21070758
4 100 loss: 0.25822794
4 200 loss: 0.20751897
5 0 loss: 0.19436865
5 100 loss: 0.24138512
5 200 loss: 0.19375528
6 0 loss: 0.18220048
00 loss: 0.3165485
2 200 loss: 0.2512417
3 0 loss: 0.23431014
3 100 loss: 0.28171945
3 200 loss: 0.22504988
4 0 loss: 0.21070758
4 100 loss: 0.25822794
4 200 loss: 0.20751897
5 0 loss: 0.19436865
5 100 loss: 0.24138512
5 200 loss: 0.19375528
6 0 loss: 0.18220048
6 100 loss: 0.22804554
更多推荐
所有评论(0)