【创作背景】
我入坑围棋的时候,正逢“李世石vs Alpha Go”人机大战。这么几年下来,学棋不成总被吊打的我,就一直有个梦想:写一个围棋AI,拿去暴打包子,报仇雪恨。于是,学完python基础语法之后,我就开始朝着深度学习方向去了。
哪吒给我推荐了《深度学习入门》这本书,他说对新手极其友好,容易看懂,于是我就从这里入坑了。全书围绕MNIST手写数据集的识别,讲解深度学习的底层理论和代码实现,确实非常友好。除了一些数学公式和理论我已经忘光光之外,啃完书的我,自认为对深度学习有了基本概念。
然后就应该上手练习了吧!我的第一个小目标:
【目标】
封装一个完整的程序,实现手写数字拍照识别。
【目标实现】
1,《深度学习入门》自带了sample_weight.kpl文件,但我找不到这个文件,权重只能自己训练。好在啃完全书,照抄代码之后,我也能自己训练出一个权重了。把这个权重存下来,留待后用。
2,官方的MNIST数据集里的图片文件是封装好的,本小白也不知道怎么拆解它。对于自己拍的照片文件,只能想办法转换成适合MNIST模型的输入数据。
3,没了,就前面两条属于新知识。
【上代码】
# 第一段:用MNIST数据集训练权重,并把权重存起来,留待后用.
这一段需要引用之前编写好的:
two_layer_net.py
mnist.py
my_fn.py(书里叫funtions.py)
这几个文件在网上都很容易找到。
# 用MNIST数据集训练权重,并把权重存起来
import numpy as np
from two_layer_net import TwoLayerNet
from mnist import load_mnist
import matplotlib.pylab as plt
from my_fn import *
import pickle
# 读取MNIST数据集
(x_train,t_train),(x_test,t_test)=load_mnist(one_hot_label=True,normalize=True)
train_loss_list=[]
train_acc_list=[]
test_acc_list=[]
epoch=0
#超参数
iters_num=5000
train_size=x_train.shape[0]
batch_size=100
learning_rate=0.1
network=TwoLayerNet(784,50,10)
# 平均每个epoch的重复次数
iter_per_epoch=max(train_size/batch_size,1)
for i in range(iters_num):
#print(i) #进程监督
batch_mask=np.random.choice(train_size,batch_size)
x_batch=x_train[batch_mask]
t_batch=t_train[batch_mask]
# 计算梯度
#grad=network.numerical_gradient(x_batch,t_batch)
grad=network.gradient(x_batch,t_batch) # 高速版
for key in ('W1','b1','W2','b2'):
network.params[key]-=learning_rate * grad[key]
loss=network.loss(x_batch,t_batch)
train_loss_list.append(loss)
if i%iter_per_epoch==0: # 每经历一个epoch(训练次数整除epoch)
epoch+=1
train_acc=network.accuracy(x_train,t_train)
test_acc=network.accuracy(x_test,t_test)
train_acc_list.append(train_acc)
test_acc_list.append(test_acc)
print('epoch: '+str(epoch)+' train acc, test acc | '+str(train_acc)+', '+str(test_acc))
# 训练好的权重存起来
file_name='sample_weight.pkl'
with open (file_name,'wb') as f:
pickle.dump(network.params,f,-1)
进入第二段之前,我们可以先看看这个权重的质量:
# 画损失函数
x=range(iters_num)
y=train_loss_list
plt.plot(x,y)
plt.xlabel('learning time')
plt.ylabel('value of loss function')
plt.show()
# 画出识别精度
x=range(len(train_acc_list))
y1=train_acc_list
y2=test_acc_list
plt.plot(x,y1,label='train_acc')
plt.plot(x,y2,linestyle='--',label='test_acc')
plt.xlabel('epochs')
plt.ylabel('accurancy')
plt.legend()
plt.show()
嗯,看上去很不错的样子。
#第二段:处理手写数字图片(可以自己写)
from PIL import Image
with open('sample_weight.pkl','rb') as f:
network0=pickle.load(f)
#使用sample_weight里存好的数据
W1,W2,b1,b2=network0['W1'],network0['W2'],network0['b1'],network0['b2']
# 读取图片,调整为正确格式,转换成 1*784 的数列
def load_pic(fn):
img0=Image.open(fn).convert('L')
if img0.size[0] !=28 or img0.size[1]!=28:
img0.resize((28,28))
arr = []
for i in range(28):
for j in range(28):
pixel = 1-float(img0.getpixel((j, i)))/255.0
arr.append(pixel)
arr1=np.array(arr)
return arr1
# 调用刚才存好的权重,识别输入的图片(数列),返回识别结果
def read_pic(x):
with open('sample_weight.pkl','rb') as f:
network=pickle.load(f)
W1,W2,b1,b2=network['W1'],network['W2'],network['b1'],network['b2']
a1=np.dot(x,W1)+b1
z1=sigmoid(a1)
a2=np.dot(z1,W2)+b2
y=softmax(a2)
max=np.max(y)
n=0
for i in y:
if i!=max:
n+=1
else:
print('晕晕狗识别结果:'+str(n),end=' ')
break
return n
# 识别单张图片
k=input('输入文件名,不带后缀:')
fn='C:/Users/Echo/Desktop/test_pic/'+k+'.png'
x=load_pic(fn)
read_pic(x)
效果(截图):
看上去很不错!!!然而——
# 小规模图片测试
acc=0
for k in range(10):
fn='C:/Users/Echo/Desktop/test_pic/'+str(k)+'.png'
print('正解:'+str(k)+' ',end='')
x=load_pic(fn)
res=read_pic(x)
if k==res:
print('正确')
acc+=1
else:
print('错误')
acc=acc/10 *100
print('总体正确率:'+str(acc)+'%')
我把我自己手写的0-9全部测试了一遍,喜提50%的惨淡正确率 (爆哭。。。)
【感想】
不管怎么说,经过了一上午的努力,把书本里的知识封装成了自己的程序,这中间,也去百度学习了很多之前并不会的知识点(比如图片转像素,比如自制权重PKL文件)。
为什么训练完毕的权重在测试图片(1w张)里的表现很好——识别精确度达到97%,但用在我自制的手写数字图片上准确度只有50%呢?不知道是我的权重出了问题,还是程序里其他代码出了问题。——这个问题,就留给饭后解决吧!(啊,肚子好饿)