前言
本文旨在通过Python编程角度进行机器学习神经网络的引导,需要掌握基础的全连接神经网络基础,这包括了神经网络全连接层的结构,权重模板与偏置的作用,节点的处理方法。在掌握这些知识之后,本文将从代码的角度实现一个完整的全连接神经网络,这包括了超参数的调试、优化器的选择、损失函数的选择以及损失函数的正则化。
库与函数基础
在进行库的安装前,请保证你的Python版本在3.5-3.7版本之间,否则截至目前,本文所需要的库无法支持更高版本。
库的安装
本文所需要的库为numpy与tensorflow,本节将引导安装,详细解释将在下一节进行展开,如果你已经安装了numpy与tensorflow,并能够通过Python成功调用,请跳过此节。
其实神经网络是可以只通过numpy来进行搭建的,但是需要有强大的逻辑能力以及对神经网络的了如指掌。Tensorflow是Google开发的可用于高效搭建神经网络。Tensorflow2.0版本已经推出许久,但多数文章仍在实用Tensorflow1.0,相比1.0,2.0有着更轻松简洁高效的优势,且1.0的代码并不适用于2.0。
我们通过最简洁的方法安装Tensorflow2.0
如果你的设备在对应的64位系统满足了以下条件,那么将可以安装Tensorflow2.0
Python 3.5–3.7
Ubuntu 16.04 或更高版本(Linux操作系统)
Windows 7 或更高版本(windows操作系统)
macOS 10.12.6 (Sierra) 或更高版本(不支持 GPU)(Apple操作系统)
Raspbian 9.0 或更高版本(树莓派操作系统)
打开命令运行指示器或者Win+R输入cmd后,输入pip install tensorflow -i https://pypi.tuna.tsinghua.edu.cn/simple
待其安装完成后,打开Python 输入 import tensorflow 运行代码
运行成功即安装完成
注:如果你在有Nvidia显卡支持的情况下想提高运算速度可以考虑GPU版本的tensorflow
sklearn包中的数据集将用来作为本次引导的数据,所以你可以下载一个sklearn包
打开命令运行指示器或者Win+R输入cmd后,输入pip install sklearn -i https://pypi.tuna.tsinghua.edu.cn/simple
基础函数介绍
关于张量:
0维即数 (1)一维即向量([1,2,3]) 二维即矩阵([[1,2,3] [1,2,3])
tensorflow2.0的运算:
import tensorflow as tf
w = [1,2,3]
b=[0,1,0]
w_pow = tf.pow(w,3) # w的三次方
w_add = tf.add(w,b) # w与b相加
w_mult = tf.multiply(w,b) # w与b 对应元素相乘
w_sub = tf.subtract(w,b) # w与b相减
w_sum = tf.reduce_sum (w) # w的最大值
w_mean = tf.reduce_mean(w) # w的平均值
print(w_pow,'\n',w_add,'\n',w_mult,'\n======================\n',w_sub,'\n',w_sum,'\n',w_mean)
输出结果:
tf.Tensor([ 1 8 27], shape=(3,), dtype=int32)
tf.Tensor([1 3 3], shape=(3,), dtype=int32)
tf.Tensor([0 2 0], shape=(3,), dtype=int32)
======================
tf.Tensor([1 1 3], shape=(3,), dtype=int32)
tf.Tensor(6, shape=(), dtype=int32)
tf.Tensor(2, shape=(), dtype=int32)
tensorflow.cast
tensorflow中在进行张量运算时,如加减乘除,需要保持运算张量之间的类型是一致的。
tensorflow中的cast实现了这个作用,它将指定参数强制转换为自定义类型。
import tensorflow #导入tensorflow
y=[1.0,2.0,3.0] #实例化一个浮点数列表
y_=tensorflow.cast(y,dtype = tensorflow.int32) #将y转换为整数32位
print(y_)
输出结果:tf.Tensor([1 2 3], shape=(3,), dtype=int32)
tensorflow.data.Dataset.from_tensor_slices
将多个数据集一一对应关系进行打包,一般用在输入与输出的对应打包上,如 一个x输入,对应了一个y输出,将x与y达成一个包。
import tensorflow
x=[1,2,3] #定义一个输入
y=[11,12,13] #定义对应的输出结果
z = tensorflow.data.Dataset.from_tensor_slices((x,y)) #将x与对应y打包
for x,y in z: #遍历的形式打印x与对应的y
print(x,y)
输出结果:
tf.Tensor(1, shape=(), dtype=int32) tf.Tensor(11, shape=(), dtype=int32)
tf.Tensor(2, shape=(), dtype=int32) tf.Tensor(12, shape=(), dtype=int32)
tf.Tensor(3, shape=(), dtype=int32) tf.Tensor(13, shape=(), dtype=int32)
tensorflow.Variable
将张量变为可更改类型,这是为了神经网络中权重和偏置参数更新中用到的。
import tensorflow
x=[1,2,3] #定义一个一维数组
z = tensorflow.Variable(x) #将张量x更改为可变更类型
print(z)
输出结果:
<tf.Variable 'Variable:0' shape=(3,) dtype=int32, numpy=array([1, 2, 3])>
tensorflow.random.truncated_normal
生成一组自定义大小符合正态分布的随机数,并且这些随机数分布比较密集
import tensorflow
w = tensorflow.random.truncated_normal([4,3],mean=0,stddev=0.1) #建立一个四行三列的矩阵,里面有12随机数,标准偏差为0.1,均值为0
print(w)
输出结果:(结果不唯一,但格式一致)
<tf.Variable 'Variable:0' shape=(4, 3) dtype=float32, numpy=
array([[ 0.10699165, 0.06251 , 0.06765407],
[-0.09613786, 0.07072052, 0.0793134 ],
[ 0.02101715, -0.12745002, 0.00702572],
[ 0.14449136, -0.06593729, -0.08573701]], dtype=float32)>
tensorflow.GradientTape.gradient
用于求解偏导数,用于神经网络中的梯度下降
使用方法如下:
import tensorflow
with tensorflow.GradientTape() as tape:
w = tensorflow.Variable([1.,2.,3.]) #定义了一个可以改变的w张量,注意是float类型
loss = tensorflow.pow(w,2) #定义了一个函数loss等于W的平方即:loss = w^2
z = tape.gradient(loss,w) #求loss,对w的导数 应为loss‘ = 2 w
print(z)
输出结果:
tf.Tensor([2. 4. 6.], shape=(3,), dtype=float32)
Python角度实现
import tensorflow as tf
import numpy as np
from sklearn import datasets
#从sklearn中载入数据和对应标签,作为输入集和输出集
#有四个输入对应一个输出,输出分为0,1,2三个数值
#共有150组数据
x_data = datasets.load_iris().data
y_data = datasets.load_iris().target
#为了得出的随机数一致,使用了随机种子,以下代码是打乱数据的顺序
np.random.seed(110)
np.random.shuffle(x_data)
np.random.seed(110)
np.random.shuffle(y_data)
tf.random.set_seed(110)
#选取前120组作为训练,后30组作为输出
x_train = x_data[:-30]
x_test = x_data[-30:]
y_train = y_data[:-30]
y_test = y_data[-30:]
#由于输入数据涉及到与权重和偏置的运算,所以需要转换为相同的数据类型
x_train = tf.cast(x_train,tf.float32)
x_test = tf.cast(x_test,tf.float32)
#将输入与对应输出进行打包,并且每32组数据为一个大包,过后再进行训练时是以一个大包为单位喂入的
train_db = tf.data.Dataset.from_tensor_slices((x_train,y_train)).batch(32)
test_db = tf.data.Dataset.from_tensor_slices((x_test,y_test)).batch(32)
#定义权重与偏置,并设定为可改变类型
w1 = tf.Variable(tf.random.truncated_normal([4,3],stddev=0.1))
b1 = tf.Variable(tf.random.truncated_normal([3],stddev=0.1))
#超参数的定义
w_m = 0 #一阶动量W(动量是与优化器的选择有关,这里选用了最复杂的自适应算法)
b_m = 0 #一阶动量b
w_v = 0 #二阶动量W
b_v = 0 #二阶动量b
beta = 0.9 #一阶动量参数
beta1 = 0.999 #二阶动量参数
lr = 0.1 #学习率
train_loss_result = []
epoch = 300 #设置迭代次数为300次后停止
loss_all = 0
test_acc =[]
global_step = 0
for epoch in range(epoch):
for step, (x_train,y_train) in enumerate(train_db):
global_step +=1
with tf.GradientTape() as tape:
y = tf.matmul(x_train,w1)+b1 #节点运算
y = tf.nn.softmax(y)#使用softmax输出可以使输出的结果以概率形式输出
y_=tf.one_hot(y_train,depth=3) #将训练集的输出以独热码的形式转换即非1即0
loss = tf.reduce_mean(tf.square(y_-y))#计算这一个大包(32组数据)的平均损失
loss_all +=loss.numpy()#将loss转换为数字并计算总损失
#对w1,b1分别求偏导数
grads = tape.gradient(loss,[w1,b1])
#以下为自适应算法的参数自更新
w_m = beta * w_m +(1-beta) * grads[0]
b_m = beta * b_m +(1-beta) * grads[1]
w_v = beta1 * w_v + (1-beta1) * tf.square(grads[0])
b_v = beta1 * b_v + (1-beta1) * tf.square(grads[1])
w_m_co = w_m/(1 - tf.pow(beta,int(global_step)))
b_m_co = b_m/(1 - tf.pow(beta,int(global_step)))
w_v_co = w_v/(1 - tf.pow(beta1,int(global_step)))
b_v_co = b_v/(1 - tf.pow(beta1,int(global_step)))
#权重和偏置的更新
w1.assign_sub(lr*w_m_co/tf.sqrt(w_v_co))
b1.assign_sub(lr*b_m_co/tf.sqrt(b_v_co))
print('Epoch{},loss:{}'.format(epoch,loss_all/4))
train_loss_result.append(loss_all/4)
loss_all=0
total_correct,total_number = 0,0
#验证集测试
for x_test,y_test in test_db:
y = tf.matmul(x_test,w1)+b1
y = tf.nn.softmax(y)
pred = tf.argmax(y,axis=1) #选出预测的概率最大的数
pred = tf.cast(pred,dtype=y_test.dtype)#将这个数转换为测试集一样的类型
correct = tf.cast(tf.equal(pred,y_test),dtype=tf.int32)#判断是否相等,如果相等转换为1,不相等转换为0
correct = tf.reduce_sum(coorect) #所有正确的数进行求和
total_correct += int(coorect)
total_number += x_test.shape[0]
#计算准确率
acc = total_correct/total_number
test_acc.append(acc)
print(acc)
输出结果:
Epoch0,loss:0.2604047544300556
0.36666666666666664
Epoch1,loss:0.26367729902267456
0.6333333333333333
Epoch2,loss:0.22564220242202282
0.5666666666666667
Epoch3,loss:0.232307568192482
0.6333333333333333
Epoch4,loss:0.2221969310194254
0.6333333333333333
Epoch5,loss:0.22083056531846523
0.6333333333333333
Epoch6,loss:0.21859966963529587
0.6333333333333333
Epoch7,loss:0.19725426100194454
0.6
Epoch8,loss:0.10007304139435291
0.6333333333333333
Epoch9,loss:0.1381650622934103
0.6333333333333333
... ...
...
Epoch296,loss:0.006568035460077226
0.9
Epoch297,loss:0.006559953617397696
0.9
Epoch298,loss:0.006551918049808592
0.9
Epoch299,loss:0.0065439402824267745
0.9
Cifar10数据库的第三方引入以及断点续存问题
本章将利用Python通过Tensorflow2.0进行本地数据集Cifar10的读入以及对调优中或完成的神经网络的参数进行提取以便于有利于下次调优或用于实际应用。如果你是大陆服务器,由于限制原因,通过tensorflow的keras自己下载Cifar10数据集需要花费几个小时的时间,然而通过加速技术下载到本地只需要几秒钟,所以这一步将节省你的时间。断点续训是调整过程中必不可少的部分,它将保留最近优化中最优模型,在下次训练时会延续这个模型,对于大数据处理来讲,这也会节省大量的时间。
数据准备及函数
在这一节将引导从官网下载Cifar10数据库,并通过Python提取数据用于训练。其次,还将阐述断点续训相关的函数。
Cifar-10/Cifar-100数据库的下载及读入
通过tensorflow.keras下载的链接,可以发现它是从http://www.cs.toronto.edu/~kriz/cifar.html上选择适合版本进行下载的,这里提供了三种不同版本的下载。这样我们就可以跳过缓慢的下载进程,并且可以通过以下代码直接读取数据:
tensorflow.keras.datasets.cifar10。load_data()
你可以点击一下链接或者自行去官网下载。
CIFAR-10 python version (163 MB)
下载之后我们需要将它解压到
C:\Users\ 你的电脑名字.keras\datasets
或者
打开C盘 → 用户 →你的电脑名字 → .keras →datasets
如果在你成功安装tensorflow的情况下,你找不到 .keras,那么他可能是一个隐藏文件夹,你需要将所有隐藏文件夹显示。
在解压之后需要将你解压的文件夹更名为cifar-10-batches-py
将之前的gz文件(压缩包)更名为cifar-10-batches-py.tar.gz
进入你的Pthon,输入一下代码即可将训练集、测试集对应的数据与标签提取出来
import tensorflow as tf
cifar10 = tf.keras.datasets.cifar10
(x_train,y_train),(x_test,y_test) = cifar10.load_data()
#x_train 为训练集输入 y_train为训练集标签;x_test为验证集输入y_test为验证集标签
.
所需函数及库
tensorflow.keras.callbacks.ModelCheckpoint()
存储所训练的网络参数为skpt形式
- filepath: 输入用于保存模型的路径,必须是字符串形式,如C:\Program Files,即保存在C盘promgram Files文件中
- monitor: 监视某个数值以判断模型优略,有val_acc 、val_loss 、 acc 、 loss四个选项,acc为准确率,loss为损失。
- verbose: 详细信息,有0,1两个选项,0为不输出,1为输出详细信息
- save_best_only: 知否只保存最优,有True和False两个选项 ,根据monitor监视的数值来判断,如果是True,将实用最近一次的最优模型的参数做覆盖,即如果第三代训练产生的最优模型,后代都无法超越,只保存第三代,直到有更优的模型出现才会覆盖
- save_weights_only:是否只保存权重,有True和False两个选项,True时将只保存模型的权重不保存其他参数。
- mode: 有三种选项分别为:auto, min, max。分别代表这自动、最小、最大。当监视器monitor为val_acc\acc时选择max,当为 val_loss\loss选择min,如果选择auto,函数将根据monitor内的名称自行判断
- save_freq: 有两个选项,分别为字符串形式的‘epoch’或者是一个整数。当填入‘epoch’时,将在每一epoch结束后保存模型,使用整数时,将在整数批后保存模型参数
例如:
EPOCHS = 10
checkpoint_filepath = './checkpoint/cifar10.skpt' #路径为与此文件相同路径下checkpoint文件夹的cifar10.skpt文件中,如果没有会自动创建一个
model_checkpoint_callback = tf.keras.callbacks.ModelCheckpoint(
filepath=checkpoint_filepath,
save_weights_only=True, #只保存权重
monitor='val_acc', #监视准确率
mode='max', #以monitor监视的值最大为最优
save_best_only=True) #只保存最优
完成这一步之后,我们将设置好的参数,放入fit函数中即可实现训练后更新
tensorflow.keras.Model.fit(x_train,y_train,32,2,
validation_data=(x_test,y_test),#以上两行为参数,请忽略
callbacks=[model_checkpoint_callback])
tensorflow.keras model.load_weights()
- filepath :读取的路径
- by_name=True和False两种选项,True通过网络层的命名填入参数,未找到命名的层随机初始化参数,False通过网络结构填入参数
如:“
tensorflow.keras model.load_weights("./checkpoint/cifar10.skpt")#通过结构填入的方式将cifar10.skpt参数填入神经网络