《深度学习入门》第4章 神经网络的学习

1 第4章整体思路

神经网络的学习:神经网络存在合适的权重(w)和偏置(b),调整权重和偏置以便拟合训练数据的过程叫做“学习”;

个人理解:使用训练数据进行学习,调整参数,让模型预测得更准确,其中参数就是权重和偏置,准确度通过损失函数观察,该往什么方向调整通过损失函数的梯度决定;

神经网络学习步骤:mini-batch-->计算梯度-->更新参数;

2层神经网络代码实现思路:(TwoLayerNet类)
构造函数:提供初始化方法;
predict函数:传入x作为第1层input值,计算数组乘积(参数通过构造函数初始化),使用sigmoid激活函数([0,input])计算出中间值;中间值作为第二层的input值,计算数组乘积(参数通过构造函数初始化),使用softmax函数(化为0.0-1.0间的值)作为输出函数计算输出值y;
loss函数:返回CE损失函数计算后的值;
accuracy函数:每经过一个epoch,就计算精度
gradient函数/numerical_gradient函数:计算梯度

2 详细内容

导入部分:

常用的特征量SIFT,SURF,HOG
常用的分类器SVM,KNN
机器学习中,一般使用测试数据与训练数据两部分进行学习和实验。首先,使用训练数据学习,寻找最优的参数,然后使用测试数据评价训练得到的模型的实际能力,获得泛化能力(就是处理未被观察的数据的能力);
训练数据又叫做监督数据;
过拟合:对某个数据集过度拟合的状态;

损失函数

寻找最优参数时,要寻找使损失函数的值尽可能小的参数;寻找的过程通过损失函数的导数实现;如果导数值为负,通过使参数向正方向改变,可以减少Loss的值,若导数值为正,通过使参数向负方向改变,可以减少Loss的值。

从纯数学的角度是比较好理解的,我们要找某个函数最小值,可以求导数找驻点,然后计算最小值;当有正参数参与计算时,可以求导数,改变参数的值以获得最小值;
作者从正反两个方向回答了why的问题,即精度是和阶跃函数都不能产生连续的变化(都是离散的,比如100笔中识别成功20笔,永远是离散的数据),所以改参数没什么用,而建立函数,求导数,更能了解数据走向;我们做数学题也是这个方法,看来这是人类解决问题的一个重要方法;
损失函数-均方误差

E=½∑(y-t)² #y是output值,t是监督数据,k是维数

看上去是基于方差的实现思想,方差本来就是度量随机变量 和其数学期望 (即 均值 )之间的偏离程度。代码直接return公式就行;

损失函数-交叉熵误差CE

E=-∑tlogy #y是Output,t是正确解标签 就是自然对数取反,因为这样结果是正数

实际实现时,为了避免溢出,会在Log里加一个微小值参数delta;代码实现也是直接return公式;

mini-batch学习
就是求大量数据的Loss,然后求这些loss-value的均值;书中给出了CEloss的批次处理公式;

E=-1/n∑∑tlogy

代码实现:

import numpy as np

def cross_entropy_error(y, t):
    if y.ndim == 1:#y.ndim维度
        t = t.reshape(1, t.size)#转换成1行size列
        y = y.reshape(1, y.size)#转换成1行size列

    # 监督数据是one-hot-vector的情况下,转换为正确解标签的索引
    if t.size == y.size:
        t = t.argmax(axis=1)#数组最大索引号

    batch_size = y.shape[0]#这种写法是行数
    return -np.sum(np.log(y[np.arange(batch_size), t] + 1e-7)) / batch_size
    #公式照抄 range单参数:0-batchsize....

梯度导入-数值微分-导数
数值微分 numerical differentiation
导数表示瞬间变化量,数学中通常是取极限;计算机不能直接求极限,实现方式是选择一个比较小的值,并且将计算方法转化成中心差分的形式计算,有误差是肯定的;

数值微分有误差,为了减少误差可以计算函数的差分。利用微小的差分求导数的过程叫做数值微分。使用过小的值会造成计算机出现溢出的问题(浮点数都是有位数的),所以微小值要选择合适的值;

梯度导入-数值微分-偏导数

有多个变量的函数的导数叫偏导数

书中给出的是固定一个变量,另一个变量转化成常量用求导数的方法求偏导;
梯度

由全部变量的偏导数汇总而成的变量称为梯度;
实际上,梯度会指向各处的函数值降低的方向。更严格地讲,梯度指示的方向是各点出的函数值减少最多的方向。

从代码上看,就是return了导数的数组;

代码实现:

#梯度
def numerical_gradient(f,x):
    h = 1e-4;#0.0001
    grad = np.zeros_like(x) #生成和x相同的数组

    for idx in range(x.size):
        tmp_val = x[idx]
        x[idx] = tmp_val + h
        fxh1 = f(x)

        x[idx] = tmp_val - h
        fxh2 = f(x)#这个写法好神奇啊

        grad[idx] = (fxh1-fxh2)/(2*h)#差分公式
        x[idx] = tmp_val

    return grad

def function_2(x):
    return x[0]**2+x[1]**2#平方之和

grad = test.numerical_gradient(function_2,np.array([3.0,4.0]))
print(grad)

D:\software\Anaconda\python.exe D:\workspace_p\study\ch04\test.py 
[6. 8.]
[6. 8.]

Process finished with exit code 0

找最优参数是指损失函数最小值的参数。一般损失函数很复杂,使用梯度来寻找函数最小值的方法叫做梯度法;梯度为0的地方不一定是最小资,且当函数复杂并且呈扁平状时,学习可能进入停滞期;
虽然梯度不一定指向最小值,但是沿着梯度的方向可以最大限度减少函数的值。
梯度法中,函数的取值从当前位置沿着梯度前进,然后在新的地方重新求梯度,如此反复(类似迭代)。梯度法有梯度上升法和梯度下降法,梯度法主要是指下降法;
梯度法代码实现:

#梯度下降法
def gradient_descent(f,init_x,lr=0.01,step_num=100):#lr 学习率
    x = init_x;

    for i in range(step_num):
        grad = numerical_gradient(f,x)
        x -= lr*grad#梯度法公式 x0-lr*偏导 就是让x动起来

    return x

#发现函数中可以不写文件名,但是在外部调用必须写
init_x = np.array([-3.0,4.0])#总是忘加[]
grad = test.gradient_descent(function_2,init_x=init_x,lr=0.1,step_num=100)
print(grad)

D:\software\Anaconda\python.exe D:\workspace_p\study\ch04\test.py 
[-6.11110793e-10  8.14814391e-10]
[-6.11110793e-10  8.14814391e-10]
Process finished with exit code 0

学习率过大的话,会发散成很大的值,学习率过小,基本上没有怎么更新就结束了;像学习率这样的参数叫做超参数,是人工设定的;


代码实现(这章的代码对于我来说有点难以理解了,所以加了些注释):
两层神经网络:

# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 为了导入父目录的文件而进行的设定
from common.functions import *
from common.gradient import numerical_gradient


class TwoLayerNet:

    def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
        # 初始化权重
        self.params = {}
        self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
        self.params['b1'] = np.zeros(hidden_size)
        self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
        self.params['b2'] = np.zeros(output_size)#创建1维数组 元素个数output_size

    def predict(self, x):
        W1, W2 = self.params['W1'], self.params['W2']
        b1, b2 = self.params['b1'], self.params['b2']
    
        a1 = np.dot(x, W1) + b1
        z1 = sigmoid(a1)#cout[0,x]
        a2 = np.dot(z1, W2) + b2
        y = softmax(a2)#cout 0.0-1.0 带e的那个公式
        
        return y
        
    # x:输入数据, t:监督数据
    def loss(self, x, t):
        y = self.predict(x)
        
        return cross_entropy_error(y, t)
    
    def accuracy(self, x, t):
        y = self.predict(x)
        y = np.argmax(y, axis=1)# 取最大值所在的索引作为预测结果
        t = np.argmax(t, axis=1)# 取最大值所在的索引作为真实结果
        
        accuracy = np.sum(y == t) / float(x.shape[0])
        return accuracy
        
    # x:输入数据, t:监督数据
    def numerical_gradient(self, x, t):
        loss_W = lambda W: self.loss(x, t)
        
        grads = {}#一开始以为是递归,点进去才发现是另一个计算梯度的函数
        grads['W1'] = numerical_gradient(loss_W, self.params['W1'])
        grads['b1'] = numerical_gradient(loss_W, self.params['b1'])
        grads['W2'] = numerical_gradient(loss_W, self.params['W2'])
        grads['b2'] = numerical_gradient(loss_W, self.params['b2'])
        
        return grads 
# coding: utf-8
import sys, os
sys.path.append(os.pardir)  # 为了导入父目录的文件而进行的设定
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet#写法

# 读入数据
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)

network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

iters_num = 1000  # 适当设定循环的次数
train_size = x_train.shape[0]
batch_size = 100
learning_rate = 0.1

train_loss_list = []
train_acc_list = []
test_acc_list = []

iter_per_epoch = max(train_size / batch_size, 1)

#numpy.random.choice(a, size=None, replace=True, p=None)
#从a(只要是ndarray都可以,但必须是一维的)中随机抽取数字,并组成指定大小(size)的数组
#replace:True表示可以取相同数字,False表示不可以取相同数字
#数组p:与数组a相对应,表示取数组a中每个元素的概率,默认为选取每个元素的概率相同。

for i in range(iters_num):#10000
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]#dataset里的
    t_batch = t_train[batch_mask]
    
    # 计算梯度
    #grad = network.numerical_gradient(x_batch, t_batch)
    grad = network.gradient(x_batch, t_batch) #高速版
    
    # 更新参数
    for key in ('W1', 'b1', 'W2', 'b2'):
        network.params[key] -= learning_rate * grad[key]
    
    loss = network.loss(x_batch, t_batch)
    train_loss_list.append(loss)

    if i % iter_per_epoch == 0: #max(train_size / batch_size, 1) 每经过一个epoch,就计算识别精度
        train_acc = network.accuracy(x_train, t_train)#成功数据百分比
        test_acc = network.accuracy(x_test, t_test)
        train_acc_list.append(train_acc)#放入train_acc数组
        test_acc_list.append(test_acc)#放入test_acc数组
        print("train acc, test acc | " + str(train_acc) + ", " + str(test_acc))

# 绘制图形
markers = {'train': 'o', 'test': 's'}
x = np.arange(len(train_acc_list))
plt.plot(x, train_acc_list, label='train acc')
plt.plot(x, test_acc_list, label='test acc', linestyle='--')
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()

end

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容