利用回归方法进行手写数字识别

MNIST手写数字识别问题

数据集

包含10种数字,每种数字7000张图片,60k进行训练,10k进行测试。

每张图片是大小为28*28的每一个像素点灰度值为0-255的灰度图片,每张图片存储为[28,28,1]。

将[28*28]-> n行拼接在n-1行的末尾 -> 一维的[784]

输入和输出 Input and Output

  • x:[b,784] b为图片数量
  • prediction:(独热编码输出结果)dog = [1,0,0..],cat = [0,1,0…],fish = [0,0,1…]
  • 独热编码 One-Hot:每一种类别拥有一个具体的节点输出,每一个节点输出一个值(归一化为0-1之间的值,值的意义为属于该类别的概率),所有节点输出值的和为1,最大概率理解为当前类别的置信度,取最大概率节点的分类作为当前物体识别的结果。

使用回归方法进行分类

  • Regression:y = w*x +b 输出为实数范围 线性模型

  • Classification: out= X@W + b (矩阵乘法) 输出为向量

  • eg:4分类,输出为5维,长度为4的向量[0.1,0.8,0.02,0.08] 则预测类别为值为0.8节点所代表的类别

MINST具体问题

线性模型方法 Linear

  • out = X@W + b

  • X:[b,784] W:[784,10] b:[10] -> out:[1,10]

作为图片识别的的逻辑是非常复杂的,线性模型是不能完成手写数字识别这种复杂的任务。所以我们引入非线性因子,添加非线性的f函数,非线性函数才能引入非线性因子。

  • out = f(X@W + b) 我们称f函数为激活函数
  • 常见激活函数:ReLU(非常简单但非线性)

加入relu激活函数后对于图片数据集还是太简单,所以添加隐藏层

X,W,b1 ->

h1 = relu(X@W1+ b1) ->

h2 = relu(h1@W2 + b2) ->

out = relu(h2@W3 + b3) -> out

实际计算过程

X = [1,784]

h1 = relu(X@W1+ b1)

W1:[784,512],b1:[1,512] -> h1:[1,512] 一个降维的过程

h2 = relu(h1@W2 + b2)

W2:[512,256],b1[1,256] -> h2:[1,256]

out = relu(h2@W3 + b3)

W3:[256,10],b3[1,10] -> out:[1,10] eg:[0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.8]

Loss 优化参数

out:[1,10]

Y/label:0~9 eg: 1-> [0,1,0,0,0,0,0,0,0,0] (one-hot 编码)

欧式距离 Euclidean Distance:out->lable

目的使out越来越接近y,使用均方误差MSE,或者说欧式距离

eg:[0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.1,0.8]-> [0,0,0,0,0,0,0,0,0,1]

整体步骤

  1. 计算 [h1,h2,out]
  2. 计算 Loss
  3. 计算梯度并优化[W1‘,b1’,W2’,b2’,W3’,b3’]
  4. 循环

使用Tensorflow进行代码测试

数据准备

通过datasets可以直接获取mnist数据集,这里我们只获取(xs,ys)为前60000张图片

1
2
3
4
5
6
7
8
9
10
11
12
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import datasets, layers , optimizers

(xs, ys),_ = datasets.mnist.load_data() #返回numpy格式
print('datasets',xs.shape,ys.shape)

xs = tf.convert_to_tensor(xs ,dtype = tf.float32) / 255
train_dataset = tf.data.Dataset.from_tensor_slices((xs,ys)) #转换出dataset类型,为了一次完成多个样本:[b,784]

for step , (x,y) in enumerate(db):# db默认一次返回一张图片[1,784]
print(step,x.shape,y,y.shape)

输出:可以看到一次只返回出一个样本并且可以看到y的值以及y的shape为(),也就是1个数

1
2
3
4
5
6
7
8
9
datasets (60000, 28, 28) (60000,) #x的shape,y的shape
0 (28, 28) tf.Tensor(5, shape=(), dtype=uint8) ()
1 (28, 28) tf.Tensor(0, shape=(), dtype=uint8) ()
2 (28, 28) tf.Tensor(4, shape=(), dtype=uint8) ()
.....
3839 (28, 28) tf.Tensor(5, shape=(), dtype=uint8) ()
3840 (28, 28) tf.Tensor(7, shape=(), dtype=uint8) ()
3841 (28, 28) tf.Tensor(6, shape=(), dtype=uint8) ()
...

引入batch操作,一次可以提取多个样本,在转换dataset类型是加入.batch()方法

1
db = tf.data.Dataset.from_tensor_slices((xs,ys)).batch(32) 

输出为:可以看到y的shape为(32, )

1
1866 (32, 28, 28) tf.Tensor([0 1 2 3 4 7 8 9 2 2 4 0 7 3 5 4 1 8 0 5 2 7 2 3 6 2 1 7 7 9 2 4], shape=(32,), dtype=uint8) (32,)

模型准备

1
2
3
4
5
6
7
8
model = keras.Sequential(
[
layers.Dense(512,activation = 'relu'), #Dense 全连接
layers.Dense(256,activation = 'relu'),
layers.Dense(10)
]
)
optimizer = optimizers.SGD(learning_rate = 0.001) #优化器 不需要人为对每一个参数设定更新规则

前向运算 计算h1、2、3

1
2
3
4
with tf.GradientTape() as tape:
x =tf.reshape(x,(-1,28*28))
out = model(x) #进行运算[b,784]->[b,10]
loss = tf.reduce_sum(tf.square(out - y))/x.shape[0]

更新

1
2
3
4
#自动进行求偏导不用人为求导 输入为w1, w2, w3, b1, b2, b3 返回他们按照顺序求导结果
grads = tape.gradient(loss, model.trainable_variables)
#对所有参数进行更新
optimizer.apply_gradients(zip(grads, model.trainable_variables))

循环

1
2
3
4
5
6
7
8
9
10
11
12
13
def train_epoch(epoch):
for step, (x, y) in enumerate(train_dataset): #32次
print(step, x.shape, y, y.shape)
with tf.GradientTape() as tape:
x =tf.reshape(x,(-1,28*28))
out = model(x) #进行运算[b,784]->[b,10]
loss = tf.reduce_sum(tf.square(out - y))/x.shape[0]
#自动进行求偏导不用人为求导 输入为w1, w2, w3, b1, b2, b3 返回他们按照顺序求导结果
grads = tape.gradient(loss, model.trainable_variables)
#把在loss中更新的梯度更新到权值当中去
optimizer.apply_gradients(zip(grads, model.trainable_variables))
if step %100 == 0:
print(epoch,step,loss.numpy())

完整结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import datasets, layers , optimizers

(x, y), (x_val, y_val) = datasets.mnist.load_data() #返回numpy格式
x = tf.convert_to_tensor(x, dtype=tf.float32) / 255.
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_dataset.batch(32)#转换出dataset类型,为了一次完成多个样本:[b,784]

# for step , (x,y) in enumerate(db):# db默认一次返回一张图片[1,784]
# print(step,x.shape,y,y.shape)

model = keras.Sequential(
[
layers.Dense(512,activation = 'relu'), #Dense 全连接
layers.Dense(256,activation = 'relu'),
layers.Dense(10)
]
)
optimizer = optimizers.SGD(learning_rate = 0.001) #优化器 不需要人为对每一个参数设定更新规则

def train_epoch(epoch):
for step, (x, y) in enumerate(train_dataset): #60000/32 次

with tf.GradientTape() as tape:
x =tf.reshape(x,(-1,28*28))
out = model(x) #进行运算[b,784]->[b,10]
loss = tf.reduce_sum(tf.square(out - y))/x.shape[0]
#自动进行求偏导不用人为求导 输入为w1, w2, w3, b1, b2, b3 返回他们按照顺序求导结果
grads = tape.gradient(loss, model.trainable_variables)
#把在loss中更新的梯度更新到权值当中去
optimizer.apply_gradients(zip(grads, model.trainable_variables))
if step %100 == 0:
print(epoch,step,loss.numpy())

def train():

for epoch in range(30):

train_epoch(epoch)

if __name__ == '__main__':
train()
打赏
  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!

扫一扫,分享到微信

微信分享二维码

请我喝杯咖啡吧~

支付宝
微信