数据集

首先数据集抉择应用Cifar-10。

这个数据汇合蕴含十个类的图片,每类600032 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,示意每个样本的维度是3072x = 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_是一个Wx进行矩阵相乘,它是一个内积值。前面须要将其变成一个概率值。

而后应用激活函数(即公式中的f)对y_进行激活:

# 多分类的激活函数: e^x / sum(e^x)# [[0.01, 0.9, ..., 0.03], [...]]p_y = tf.nn.softmax(y_)

这是多分类的激活函数softmax(),而二分类的激活函数个别应用sigmoid()函数。

接着对y进行one_hot编码,这步次要是为了将yp_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 = 20train_steps = 100000test_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