深度学习笔记:01快速构建一个手写数字识别系统
神经网络代码最好运行在GPU中,但是对于初学者来说运行在GPU上成本太高了,所以先运行在CPU中,就是慢一些。
一、安装keras框架
- 使用管理员模式打开cmd,运行以下指令
C:\WINDOWS\system32>pip install tensorflow_cpu -i http://mirrors.aliyun.com/pypi/simple/ --trusted-host mirrors.aliyun.com
(以后安装也可以这样,只要将tensorflow_cpu换成需要下载的库或包名,根据自己需求自行更改即可)
安装完成:
-
检测是否安装上keras框架:
运行以下指令:
import tensorflow import keras print(keras.__version__)
我的输出结果是:2.9.0。安装完毕,可以使用啦~
二、熟悉mnist数据集
首先,我们先把我们需要的数据集引入,并且定义两个数据集
from keras.datasets import mnist
(train_images,train_labels),(test_images,test_labels) = mnist.load_data()
其中,这个images和labels分别表示我们这个mnist数据集中的图片和这个图片所对应的数字(标签),例如:
print(test_images)
结果:
这个可能并不方便看出什么来,那么我们通过:
print(test_images.shape)
结果:
可以看出这个test_images是一个含有10000个元素的数组,并且每个元素是一个28×28的二维数组(像素)。也就是这个test_images含有10000张像素为28×28的手写数字图片。
下面我们再看一下test_labels:
print(test_labels)
结果:
test_labels是标记我们test_images的标签,他也是有10000个元素并且每个元素都和test_images的元素一一对应。相当于告诉我们test_images每个图片对应的手写数字是什么。
我们可以通过下面几行代码做一个简短的验证:
digit = test_images[0]
import matplotlib.pyplot as plt # matpoltlib是一个绘制图案的python库
plt.imshow(digit,cmap=plt.cm.binary)
plt.show()
结果:
确实,test_labels也显示第一个数字是7。验证完毕~
三、使用keras框架快速构建手写数字识别函数
首先我们之前提过神经网络的模型结构:
下面这段代码完成的是这个模型结构的上半部分:
from keras import models
from keras import layers
network = models.Sequential()
network.add(layers.Dense(512,activation="relu",input_shape=(28*28,)))
# activation="relu"的意思是激活函数是relu函数 input_shape=(28*28,)的意思是我接受的这个神经网络必须是28*28的二维数组
network.add(layers.Dense(10,activation="softmax"))
**Sequential()函数的作用:**将你的神经网络的每一层都想象成一个佛珠,然后把每个你想串联起来的网络层名字传给Sequential函数,他的作用就是把每个网络层像串佛珠一样串联起来,这样你就不用每个都去链接一次了。
下面我们再来完善我们这个模型的下半部分:
network.compile(optimizer="rmsprop",loss="categorical_crossentropy",metrics=["accuracy"])
**compile()函数的作用:**用于将源代码编译为代码对象或AST模块对象。 可以根据提供的模式使用exec()或eval()函数执行返回的代码对象,以构造代码对象。
那么我们这一行就对应着这个网络模型的下半部分。
下面我们在对我们images数据进行进一步的处理:
train_images = train_images.reshape((60000,28*28))
train_images = train_images.astype('float32')/255
test_images = test_images.reshape(10000,28*28)
test_images = test_images.astype('float32')/255
将我们原来的train_images里面28×28的二维数组变成28×28的一维数组
再将每一个元素里面的数值都除以255(因为像素点)
因为我们传给网络进行训练的图片是一个灰度图,灰度图指的是这里面的每一个像素点他的值都介于0~255之间。所以除以255以后他的每个数都变成了0~1之间的一个浮点数,这样传给网络便于接下来的识别与统计。
接下来我们来转化labels数据:
from keras.utils import to_categorical
print("before change:",test_labels[0])
train_labels = to_categorical(train_labels)
test_labels = to_categorical(test_labels)
print("after change:",test_labels[0])
关键点:调用categorical接口转换
目的:将数字变成数组表示。因为图片中包含的都是0~9一共10个数字,下面我们将这个数字变成由10个元素组成的数组。比如说刚刚我们的数字7,将其变成数组以后就其余元素都是0只有数组的第七个元素是1。然后将这个转换给我们的神经网络进行识别。
结果:
最后一步,开始训练我们的神经网络:
network.fit(train_images,train_labels,epochs=5,batch_size=128)
现在我们准备开始训练网络,在 keras中这一步是通过调用网络的fit方法来完成的—— 我们在训练数据上拟合(fit)模型。
epochs表示每次训练改进时都是进行5个循环
batch_size=128表示每次训练都从这个数组中_images随机抽取128个图片来进行统计
结果如图:
训练过程中显示了两个数字:一个是网络在训练数据上的损失(loss),另一个是网络在 训练数据上的精度(acc)。
下面我们调用evaluate接口看我们对这个测试的图片进行识别看识别成功率为多少
test_loss,test_acc=network.evaluate(test_images,test_labels,verbose=1)
print('test_acc:',test_acc)
我这边是:
最后我们拿一张图片,来看一下这个对于这张图片我们的神经网络的识别结果:
from keras.datasets import mnist
(train_images,train_labels),(test_images,test_labels) = mnist.load_data()
digit = test_images[1]
plt.imshow(digit,cmap=plt.cm.binary)
plt.show()
test_images = test_images.reshape(10000,28*28)
res = network.predict(test_images)
print(res[1])
for i in range(res[1].shape[0]):
if(res[1][i] == 1):
print("the number for the picture is",i)
break
结果很不错!成功识别是2
四、张量的概念
tensor的中文意思就是:张量
tensor是后面在进行神经网络开发时的最基本的概念,存储在多维数组(Numpy)中。
tensor的常用属性有维度(dim),和形状(shape)
- 0-dim的tensor就是一个常量,在python中,我们通过ndim的属性来获取tensor的维度,通过shape属性来获取形状
import numpy as np
x= np.array(666)
print(x)
print(x.ndim)
print(x.shape)
666
0
()
- 1-dim的tensor就是一个数组
x= np.array([0,1,4,9])
print(x)
print(x.ndim)
print(x.shape)
[0 1 4 9]
1
(4,)
- 2-dim的tensor就是一个二维数组
x = np.array([
[1,2,3,4],
[2,2,3,4],
[3,2,3,4]
])
print(x)
print(x.ndim)
print(x.shape)
[[1 2 3 4]
[2 2 3 4]
[3 2 3 4]]
2
(3, 4)
所以我们可以理解为一个n-dim的tensor,他其实就是一个一维数组,数组中每个元素都是(n-1)-dim的tensor。所以,3-dim张量就是一个一维数组,数组中的每个元素就是2-dim的tensor,相关代码如下:
x = np.array([
[
[1,2],
[3,4]
],
[
[5,6],
[7,8]
],
[
[9,1],
[1,2]
]
])
print(x.ndim)
print(x.shape)
3
(3, 2, 2)
shape告诉我们,这个x有三个元素,每个元素都是一个2*2的二维数组
几维张量括号内就对应几个数字:
- 第一个数字3:表示这个3-张量内包括3个元素,即2-dim张量
- 第二个数字2:2-dim张量内包含2个元素,即1-dim张量
- 第三个数字2:1-dim张量内包含2个元素,即0-dim张量(常量)
所以对照我们上节所学train_images:
所以在面对大规模的张量时我们可以实现另一种操作就是将张量中的某一部分的值单独提取出来:
from keras.datasets import mnist
(train_images,train_labels),(test_images,test_labels) = mnist.load_data()
my_slice = train_images[10:100]
print(my_slice.shape)
![image-20220802210719852](https://orank.oss-cn-qingdao.aliyuncs.com/202208022107891.png)
这就是batch的概念:batch值得是从全部数据中抽出一部分形成一个子集,上面代码中的my_slice就是一个batch
五、张量的运算
张量的relu运算:将张量内的值x与0作比较,如果x>0 不变,如果x<0 则将x变成0
类似下面的函数实现:
def naive_relu(x):
assert len(x.shape) == 2
x = x.copy() #确保操作不对x产生影响
for i in range(x.shape[0]):
for j in range(x.shape[1]):
x[i][j] = max (x[i][j],0)
return x
x = np.array([
[1,-1],
[-2,1]
])
print(naive_relu(x))
![image-20220802211754771](https://orank.oss-cn-qingdao.aliyuncs.com/202208022117815.png)
==张量的点乘运算:==1-dim的点乘是每个之间点乘并求和累加
类似下面的函数实现:
def naive_vector_dot(x,y):
assert len(x.shape) == 1
assert len(y.shape) == 1
assert x.shape[0] == y.shape[0]
z = 0.
for i in range(x.shape[0]):
z = z + x[i] * y[i]
return z
x = np.array([1,2,3])
y = np.array([4,5,6])
print(naive_vector_dot(x,y))
![image-20220802212645578](https://orank.oss-cn-qingdao.aliyuncs.com/202208022126618.png)
所以类推高维张量之间做点乘运算,类似于矩阵的运算。
即:一个m*n的矩阵也只能乘以一个n*s的矩阵,这俩矩阵第一个的列数必须得等于第二个的行数
例如:
def naive_matrix_vector_dot(x,y):
z = np.zeros(x.shape[0])
for i in range(x.shape[0]):
z[i] = naive_vector_dot(x[i,:],y)
return z
x = np.array([
[4,5,6],
[7,8,9]
])
y = np.array(
[1,2,3]
)
print(naive_matrix_vector_dot(x,y))
![image-20220802214958580](https://orank.oss-cn-qingdao.aliyuncs.com/202208022149650.png)
转化成矩阵运算也就是这个意思:
![image-20220802215031056](https://orank.oss-cn-qingdao.aliyuncs.com/202208022150212.png)
最后,张量是深度学习中最基础的一个概念。务必要搞清楚!