python: MNIST-神经网络的自主学习

问题:如何实现数字“5”的识别?O(∩_∩)O~

                                 手写数字“5”的例子:写法因人而异,五花八门

方案: 从图像中提取特征量-----用及其学习技术学习这些特征量的模式

神经网络的学习中所用到的指标称为损失函数

可以用作损失函数的函数很多,最有名的是均方误差(mean squared error)。

  均方误差的表达式:

    

    yk  -----------神经网络输出

    tk  -----------监督数据,one-hot表示

  这里的神经网络的输出y是softmax函数的输出。softmax函数的输出可以理解为概率

  用python实现均方误差:   

def mean_squared_error(y-t):
    return 0.5*np.sum((y-t)**2)

除了均方误差,交叉熵误差(cross entropy error)也经常被用作损失函数。
  交叉熵误差的表达式:

    

    比如,假设正确解标签的索引是“2”,与之对应的神经网络的输出是 0.6,则交叉熵误差是 -log 0.6 = 0.51;若“2”对应的输出是 0.1,则交叉熵误差为 -log 0.1 = 2.30。也就是说,交叉熵误差的值是由正确解标签所对应的输出结果决定的

自然对数的图像如图所示:

 1 import matplotlib.pyplot as plt
 2 import numpy as np
 3 
 4 #生成数据
 5 x=np.arange(0.01,1.01,0.01)
 6 y=np.log(x)
 7 
 8 #绘制图像
 9 plt.plot(x,y)
10 plt.xlabel('x')
11 plt.ylabel('y')
12 plt.show()

代码实现交叉熵误差:

def cross_entropy_error(y,t):
    delta=1e-7
    return -np.sum(t*np.log(y+delta))

 

如果计算所有训练数据的损失函数的综合,则公式为:

  

上述的损失函数都是针对单个数据,加入训练数据一共有6000个,那我们每次拿出100个当做是6000的近似计算一次,这样计算得到的结果就是mini-batch学习的结果。

在之前的笔记中已经知道了读入MNIST数据集的代码,即:

import sys, os
sys.path.append(os.pardir)
import numpy as np
from dataset.mnist import load_mnist

(x_train, t_train), (x_test, t_test) = 
    load_mnist(normalize=True, one_hot_label=True)

print(x_train.shape) # (60000, 784)
print(t_train.shape) # (60000, 10)

mnist数据下载的代码mnist.py如下:

  1 # coding: utf-8
  2 try:
  3     import urllib.request
  4 except ImportError:
  5     raise ImportError('You should use Python 3.x')
  6 import os.path
  7 import gzip
  8 import pickle
  9 import os
 10 import numpy as np
 11 
 12 
 13 url_base = 'http://yann.lecun.com/exdb/mnist/'
 14 key_file = {
 15     'train_img':'train-images-idx3-ubyte.gz',
 16     'train_label':'train-labels-idx1-ubyte.gz',
 17     'test_img':'t10k-images-idx3-ubyte.gz',
 18     'test_label':'t10k-labels-idx1-ubyte.gz'
 19 }
 20 
 21 dataset_dir = os.path.dirname(os.path.abspath(__file__))
 22 save_file = dataset_dir + "/mnist.pkl"
 23 
 24 train_num = 60000
 25 test_num = 10000
 26 img_dim = (1, 28, 28)
 27 img_size = 784
 28 
 29 
 30 def _download(file_name):
 31     file_path = dataset_dir + "/" + file_name
 32     
 33     if os.path.exists(file_path):
 34         return
 35 
 36     print("Downloading " + file_name + " ... ")
 37     urllib.request.urlretrieve(url_base + file_name, file_path)
 38     print("Done")
 39     
 40 def download_mnist():
 41     for v in key_file.values():
 42        _download(v)
 43         
 44 def _load_label(file_name):
 45     file_path = dataset_dir + "/" + file_name
 46     
 47     print("Converting " + file_name + " to NumPy Array ...")
 48     with gzip.open(file_path, 'rb') as f:
 49             labels = np.frombuffer(f.read(), np.uint8, offset=8)
 50     print("Done")
 51     
 52     return labels
 53 
 54 def _load_img(file_name):
 55     file_path = dataset_dir + "/" + file_name
 56     
 57     print("Converting " + file_name + " to NumPy Array ...")    
 58     with gzip.open(file_path, 'rb') as f:
 59             data = np.frombuffer(f.read(), np.uint8, offset=16)
 60     data = data.reshape(-1, img_size)
 61     print("Done")
 62     
 63     return data
 64     
 65 def _convert_numpy():
 66     dataset = {}
 67     dataset['train_img'] =  _load_img(key_file['train_img'])
 68     dataset['train_label'] = _load_label(key_file['train_label'])    
 69     dataset['test_img'] = _load_img(key_file['test_img'])
 70     dataset['test_label'] = _load_label(key_file['test_label'])
 71     
 72     return dataset
 73 
 74 def init_mnist():
 75     download_mnist()
 76     dataset = _convert_numpy()
 77     print("Creating pickle file ...")
 78     with open(save_file, 'wb') as f:
 79         pickle.dump(dataset, f, -1)
 80     print("Done!")
 81 
 82 def _change_one_hot_label(X):
 83     T = np.zeros((X.size, 10))
 84     for idx, row in enumerate(T):
 85         row[X[idx]] = 1
 86         
 87     return T
 88     
 89 
 90 def load_mnist(normalize=True, flatten=True, one_hot_label=False):
 91     """读入MNIST数据集
 92     
 93     Parameters
 94     ----------
 95     normalize : 将图像的像素值正规化为0.0~1.0
 96     one_hot_label : 
 97         one_hot_label为True的情况下,标签作为one-hot数组返回
 98         one-hot数组是指[0,0,1,0,0,0,0,0,0,0]这样的数组
 99     flatten : 是否将图像展开为一维数组
100     
101     Returns
102     -------
103     (训练图像, 训练标签), (测试图像, 测试标签)
104     """
105     if not os.path.exists(save_file):
106         init_mnist()
107         
108     with open(save_file, 'rb') as f:
109         dataset = pickle.load(f)
110     
111     if normalize:
112         for key in ('train_img', 'test_img'):
113             dataset[key] = dataset[key].astype(np.float32)
114             dataset[key] /= 255.0
115             
116     if one_hot_label:
117         dataset['train_label'] = _change_one_hot_label(dataset['train_label'])
118         dataset['test_label'] = _change_one_hot_label(dataset['test_label'])
119     
120     if not flatten:
121          for key in ('train_img', 'test_img'):
122             dataset[key] = dataset[key].reshape(-1, 1, 28, 28)
123 
124     return (dataset['train_img'], dataset['train_label']), (dataset['test_img'], dataset['test_label']) 
125 
126 
127 if __name__ == '__main__':
128     init_mnist()

minst.py在文件夹dataset中,数据在dataset下的mnist.pkl文件中。

目前我还是自己写不出来这样的代码,还是得分析透才能学会,这里先留一下,下次来分析......

那么,如何从训练数据中随机抽取10笔数据呢?可以使用NumPy的no.random.choice(),携程如下形式:

train_size=x_train.shape[0]
batch_size=10
batch_mask=np.random.choice(train_size,batch_size)
x_batch=x_train[batch_mask]
t_batch=t_train[batch_mask]


mini_batch交叉熵的实现:

def cross_entropy_error(y,t):
    if y.ndim==1:
        t=t.reshape(1,t.size)
        y=y.reshape(1,y.size)
        
    batch_size=y.shape[0]
    return -np.sum(t*np.log(y+1e-7))/batch_size

这里,y 是神经网络的输出,t 是监督数据。y 的维度为 1 时,即求单个数据的交叉熵误差时,需要改变数据的形状。并且,当输入为 mini-batch 时,要用 batch 的个数进行正规化,计算单个数据的平均交叉熵误差。

当监督数据是标签形式而不是one-hot表示时,交叉熵代码如下:

def cross_entropy_error(y,t):
    if y.ndim==1:
        t=t.reshape(1,t.size)
        y=y.reshape(1,y.size)
        
    batch_size=y.shape[0]
    return -np.sum(np.log(y[np.arange(batch_size),t]+1e-7))/batch_size

实现的要点是,由于 one-hot 表示中 t 为 0 的元素的交叉熵误差也为 0,因此针对这些元素的计算可以忽略。换言之,如果可以获得神经网络在正确解标签处的输出,就可以计算交叉熵误差。因此,t 为 one-hot 表示时通过 t * np.log(y) 计算的地方,在 t 为标签形式时,可用 np.log( y[np.arange (batch_size), t] ) 实现相同的处理(为了便于观察,这里省略了微小值1e-7)。

作为参考,简单介绍一下np.log( y[np.arange(batch_size), t] )np.arange (batch_size) 会生成一个从 0 到 batch_size-1 的数组。比如当 batch_size 为 5 时,np.arange(batch_size) 会生成一个 NumPy 数组 [0, 1, 2, 3, 4]。因为 t 中标签是以 [2, 7, 0, 9, 4] 的形式存储的,所以 y[np.arange(batch_size), t] 能抽出各个数据的正确解标签对应的神经网络的输出(在这个例子中,y[np.arange(batch_size), t] 会生成 NumPy 数组 [y[0,2], y[1,7], y[2,0], y[3,9], y[4,4]])。

这部分理解起来并不是很容易,还是得多回顾才能真正掌握。均方误差、交叉熵误差以及下载mnist数据集,这些里面后两部分还得在后续的学习中继续补充。

一定要加油啊,向目标前进~~~

我的前方是万里征途,星辰大海!!
原文地址:https://www.cnblogs.com/taoyuxin/p/11441692.html