数据集
首先数据集抉择应用 Cifar-10。
这个数据汇合蕴含 十个类
的图片,每类 6000
张32 x 32
的图片,共计 60000
张图片,其中 50000
训练图片,10000
张测试图片。
这里下载了 python
对应的版本:
读取文件
在 Cifar-10
的网站,咱们能够找到实例代码,而后依照本人的文件目录读取:
CIRFA_DIR = "../data/cifar-10-batches-py"
def load_data(filename):
# 从文件中读取数据
with open(filename, 'rb') as f:
# pickle 默认是用 ASCII 编码,然而待载入文件并不是应用 ASCII 编码的,须要申明 encoding,来正确载入文件
data = cPickle.load(f, encoding='latin1')
return data['data'], data['labels']
这里与实例代码有点不同,在 load()
函数中,增加了一个 encoding
参数,因为默认载入文件是应用 ASCII
编码,然而数据集文件并不是应用这个编码的,所以要正确关上,须要增加encoding='latin1'
,否则不能正确解码文件。
数据处理——加载 Cifar 样本
这里创立一个 CifarData
类,用来加载样本文件中的数据:
class CifarData:
def __init__(self, filenames, need_shuffle):
all_data = []
all_labels = []
for filename in filenames:
data, labels = load_data(filename)
all_data.append(data)
all_labels.append(labels)
self._data = np.vstack(all_data) # 将 data 纵向合并为一个矩阵
self._data = self._data / 127.5 - 1 # 将 data 作一个归一化,0 到 1 之间
self._labels = np.hstack(all_labels) # 将 labels 横向合并为一个矩阵
self._num_examples = self._data.shape[0] # 样本数
self._need_shuffle = need_shuffle # 保留 need_shuffle
self._indicator = 0 # 指明在以后数据集,把这个数据集遍历到哪个地位上
if self._need_shuffle:
self._shuffle_data()
留神到这里在 init
函数中,定义了一个 need_shuffle
的变量。
对于训练集来说,须要在训练一遍之后进行 数据的打乱
,也就是shuffle
,以防程序数据产生的 过拟合
问题;
对于测试集来说,不在测试集上进行训练,只计算 损失函数
和准确率
,也就不须要进行shuffle
。
而后在这个类里,还须要定义两个函数:
# 从新打乱数据
def _shuffle_data(self):
# 对_num_example 这些数据进行混排
p = np.random.permutation(self._num_examples)
self._data = self._data[p]
self._labels = self._labels[p]
首先是 shuffle
函数,来对训练集数据进行打乱。
# 每次返回 batch_size 个样本
def next_batch(self, batch_size):
end_indicator = self._indicator + batch_size # 定义每个 batch 的完结地位
# 如果完结地位大于全副的数组
if end_indicator > self._num_examples:
# 如果能够 shuffle,if self._need_shuffle:
self._shuffle_data()
self._indicator = 0
end_indicator = batch_size
# 数据集曾经遍历完,并且不容许进行 shuffle
else:
raise Exception("have no more examples")
# 验证 batch_size 是否比整个样本数还要大
if end_indicator > self._num_examples:
raise Exception("batch size is larger than all examples")
# 返回 indicator 到 end_indicator 之间的数据
batch_data = self._data[self._indicator: end_indicator]
batch_labels = self._labels[self._indicator: end_indicator]
self._indicator = end_indicator # 从新定位 indicator
return batch_data, batch_labels
而后定义了 next_batch()
函数,用来每次返回 batch_size
大小的数据。并在每次返回后,对残余数据进行打乱。
初始化相干变量
# 这里占位符的第一维度为 None,示意样本数是不确定的;第二维度为 3072,示意每个样本的维度是 3072
x = tf.placeholder(tf.float32, [None, 3072])
y = tf.placeholder(tf.int64, [None])
# 定义权重,并在均值为 0,方差为 1 的范畴内进行随机初始化 (3072 * 10)# 进行多分类,样本数据总共有十个类
w = tf.get_variable('w', [x.get_shape()[-1], 10],
initializer=tf.random_normal_initializer(0, 1))
# 定义偏置,初始化为常量 0 (10,)
b = tf.get_variable('b', [10], initializer=tf.constant_initializer(0.0))
# (None, 3072) * (3072, 10) = (None, 10)
y_ = tf.matmul(x, w) + b
在神经网络中,神经元的根本构造如下,x1、x2、x3...
是输出,h(W*x)
是输入,W*x
是一个两头过程,示意让每个 x 和其权重做乘积,而后再加起来,再由 h 函数
失去输入。这里只有一个输入,就示意是 单神经元
:
这外面的 w、x
就对应代码中定义的 w 和 x
,而后输入就是y
。这里还有一个 偏置 b
,这就是咱们之前数学中所学到 截距
的概念相似,也就是 分类线
或分类面
与坐标轴交点
的值:
而后 y_
是一个 W
和x
进行矩阵相乘,它是一个内积值。前面须要将其变成一个概率值。
而后应用激活函数(即公式中的 f)对 y_
进行激活:
# 多分类的激活函数: e^x / sum(e^x)
# [[0.01, 0.9, ..., 0.03], [...]]
p_y = tf.nn.softmax(y_)
这是多分类的激活函数 softmax()
,而二分类的激活函数个别应用sigmoid()
函数。
接着对 y
进行 one_hot
编码,这步次要是为了将 y
和p_y
转换成雷同的数据格式:
# 对 y 进行 ont_hot 编码,使得 y 和 p_y 放弃雷同的数据类型,以计算损失函数
# 5 -> [0, 0, 0, 0, 0, 1, 0, 0, 0, 0]
y_one_hot = tf.one_hot(y, 10, dtype=tf.float32)
最初计算损失函数:
# 计算损失函数
loss = tf.reduce_mean(tf.square(y_one_hot - p_y))
这里计算损失函数,采纳了 平方差
的形式。
最初对准确率进行计算,并对 loss
做梯度降落(梯度降落算法),找到 loss 的最小值:
predict = tf.argmax(y_, 1) # 预测值
correct_predict = tf.equal(predict, y) # 正确的预测值
accuracy = tf.reduce_mean(tf.cast(correct_predict, tf.float64)) # 准确率
# 对 loss 做梯度降落
with tf.name_scope('train_op'):
train_op = tf.train.AdamOptimizer(1e-3).minimize(loss)
进行训练
首先先执行初始化的变量,而后定义训练的 batch_size
、 训练的步数
、 测试的步数
。
# 执行初始化
init = tf.global_variables_initializer()
batch_size = 20
train_steps = 100000
test_steps = 100
接着应用 session
开始执行计算:
with tf.Session() as sess:
sess.run(init)
# 循环 train_steps,进行训练
for i in range(train_steps):
batch_data, batch_labels = train_data.next_batch(batch_size)
loss_val, accu_val, _ = sess.run([loss, accuracy, train_op],
feed_dict={
x: batch_data,
y: batch_labels})
# 打印两头过程
if (i + 1) % 500 == 0:
print('[Train] step: %d, loss: %4.5f, acc: %4.5f' % (i + 1, loss_val, accu_val))
# 应用测试集进行评测
if (i + 1) % 5000 == 0:
test_data = CifarData(test_filename, False)
all_test_acc_val = [] # 保留总的 accuracy
for j in range(test_steps):
test_batch_data, test_batch_labels = test_data.next_batch(batch_size)
test_acc_val = sess.run([accuracy], feed_dict={x: test_batch_data, y: test_batch_labels})
all_test_acc_val.append(test_acc_val)
test_acc = np.mean(all_test_acc_val) # 对 test 的 acc 做均匀
print('[Test] step: %d, acc: %4.5f' % (i + 1, test_acc))
tensorflow
只有让 计算图
上的节点在 session
中执行能力失去后果,sess
只有在调用 run()
函数后能力开始执行计算。而个别调用 run()
函数后,执行计算会非常耗费资源,所以必须要在完结后执行 close()
进行敞开:
sess.run(init)
sess.close()
然而手动进行敞开又比拟麻烦,有时候还会遗记,所以就是用 with
语句,完结后主动敞开:
with tf.Session as sess:
sess.run(init)
初始化执行后,开始循环train_steps
,进行训练。
在每执行 500 步
打印一次两头过程,输入 以后步
、 损失函数
和准确率
。
同时,为了做出相似真是环境中的评测,须要在测试集上忘性评测。每 5000 步
对已训练的模型进行一次评测。
运行后果及总结
咱们发现随着训练的进行,准确率
在整体上有肯定水平的晋升。
通过对 单个神经元
的神经网络到 多个神经元
的神经网路的学习,对实现神经网络的根本过程有了肯定水平的理解。
对于数据处理局部,归一化
解决对后果的影响非常重要:
self._data = self._data / 127.5 - 1
没有进行归一化之前,训练的准确率根本没有什么变动,这是因为 未归一化
的数据都比拟大,这就导致了运算会偏差某一方,从而导致准确率的变动不大。
其次,在解决数据的时候,还要多留神 数据类型
的关系。因为其中很多都是矩阵的乘法,一旦数据类型或格局不能匹配,就不能正确运算。
对于神经网络的解决局部,次要就是了解 特色集(x)
、损失函数 (loss)
等概念。通过编程的实现,对文章开始局部的方程的了解不仅仅限度在数学层面:
包含推 梯度降落
的利用,对激活函数的利用等。
相干参考:
https://tensorflow.google.cn/versions/r2.0/api_docs/python/tf
https://www.imooc.com/learn/1063