SGD学习算法的实现

题外话

        这是本人的第一篇作品哦,小白希望大佬们多多指教。

        最近捧读了鱼书,想养成做笔记的好习惯~

介绍

        本篇文章是对最近学习的成果进行阶段性的总结,介绍了神经网络的最基本的概念以及Python代码的实现。

        本文以两层神经网络为例,使用MNIST手写数字图像数字集。

        特别声明:本文不包括定义权重和偏置的初值以及超参数的确定等模块,仅仅是对推理过程、梯度下降法的演示

        不讲废话进入正题~


各个变量和方法的作用

        

变量
变量     说明
params                

保存神经网络参数的字典型变量

grads

保存梯度的字典型变量
方法
方法说明
__init__进行权重的初始化
predict(self,x)进行识别
loss(self,x,t)计算损失函数的值
accuracy(self,x,t)计算识别精度
numerical_gradient(self,x,t)计算权重参数的梯度

        方法的具体实现

                __init__

                        __init__函数是对权重和偏置的初始化。

    def __init__(self,input_size,hidden_size,output_size,weight_init_std=0.01):
        #input_size表示输入层的神经元数,hidden_size表示隐藏层的神经元数,output_size表示输出层的神经元数
        #初始化权重
        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)#第二层偏置(隐藏层到输出层)

                        通过代码,我们定义了第一层和第二层的权重和偏置。

                predict(self,x)

                       该方法的作用是对各层神经元的计算,根据之前所学,各个神经元的连接计算采用方法是矩阵相乘,在隐藏层需要使用激活函数(这里使用的是sigmoid函数),在输出层我们采用了softmax函数进行分类。

    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)
        a2 = np.dot(z1,W2) + b2
        y = softmax(a2)
        return y
def sigmoid(x):
    #激活函数
    return 1/(1+np.exp(-x))
def softmax(a):
    #输出层设计
    c = np.max(a)
    exp_a = np.exp(a-c)
    sum_exp_a = np.sum(exp_a)
    y = exp_a/sum_exp_a
    return y

                        通过代码,我们将数据传入后,进行神经元的推理。

              loss(self,x,t)

                        计算损失函数的值,这里损失函数我们使用的是交叉熵误差

def loss(self,x,t): #计算损失函数的值,x是图像数据,t是正确解标签
        y = self.predict(x)
        return cross_entropy_error(y,t)
def cross_entropy_error(y,t): #损失函数--交叉熵误差
    if y.ndim ==1:
        t = t.reshape(1,t.size)
        y = y.reshape(1,t.size)
    batch_size = y.shape[0]
    # h = -np.sum(t*np.log(y+1e-7))/batch_size
    # print(h)

    return -np.sum(t*np.log(y+1e-7))/batch_size

                        而我们学习的目的就是为了让损失函数的值变小。所以要沿着梯度的方向不断更新权重。

                 accuracy(self,x,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])
                numerical_gradient(self,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
def _numerical_gradient_no_batch(f, x):  #计算梯度
    h = 1e-4  # 0.0001
    grad = np.zeros_like(x)

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

        x[idx] = tmp_val - h
        fxh2 = f(x)  # f(x-h)
        grad[idx] = (fxh1 - fxh2) / (2 * h)

        x[idx] = tmp_val  # 还原值

    return grad


def numerical_gradient(f, X):
    if X.ndim == 1:
        return _numerical_gradient_no_batch(f, X)
    else:
        grad = np.zeros_like(X)

        for idx, x in enumerate(X):
            grad[idx] = _numerical_gradient_no_batch(f, x)

        return grad

两层神经元的实现

        上述了各个方法的使用,接下来就是全部代码,在该代码中使用了梯度下降法不断更新权重,学习了10000次,然后经过漫长的等待真的很久!!!(听说用误差发现传播法计算梯度很快),终于实现了!

import pickle
from mnist import load_mnist
import numpy as np

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D


def _numerical_gradient_no_batch(f, x):  #计算梯度
    h = 1e-4  # 0.0001
    grad = np.zeros_like(x)

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

        x[idx] = tmp_val - h
        fxh2 = f(x)  # f(x-h)
        grad[idx] = (fxh1 - fxh2) / (2 * h)

        x[idx] = tmp_val  # 还原值

    return grad


def numerical_gradient(f, X):
    if X.ndim == 1:
        return _numerical_gradient_no_batch(f, X)
    else:
        grad = np.zeros_like(X)

        for idx, x in enumerate(X):
            grad[idx] = _numerical_gradient_no_batch(f, x)

        return grad
def sigmoid(x):
    #激活函数
    return 1/(1+np.exp(-x))
def softmax(a):
    #输出层设计
    c = np.max(a)
    exp_a = np.exp(a-c)
    sum_exp_a = np.sum(exp_a)
    y = exp_a/sum_exp_a
    return y
def cross_entropy_error(y,t): #损失函数--交叉熵误差
    if y.ndim ==1:
        t = t.reshape(1,t.size)
        y = y.reshape(1,t.size)
    batch_size = y.shape[0]
    # h = -np.sum(t*np.log(y+1e-7))/batch_size
    # print(h)

    return -np.sum(t*np.log(y+1e-7))/batch_size

class TwoLayerNet:
    def __init__(self,input_size,hidden_size,output_size,weight_init_std=0.01):
        #input_size表示输入层的神经元数,hidden_size表示隐藏层的神经元数,output_size表示输出层的神经元数
        #初始化权重
        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)#第二层偏置(隐藏层到输出层)
    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)
        a2 = np.dot(z1,W2) + b2
        y = softmax(a2)
        return y
    def loss(self,x,t): #计算损失函数的值,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
    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

# #net测试TwoLayerNet
# net = TwoLayerNet(input_size=784,hidden_size=100,output_size=10)
# #保存该神经网络的所有参数
# print("定义神经网络的各个参数")
# print(net.params['W1'].shape)# (784, 100)
# print(net.params['b1'].shape)# (100,)
# print(net.params['W2'].shape)# (100, 10)
# print(net.params['b2'].shape)# (10,)
# #神经网络推理过程
# print("输出神经元的推理过程得到结果")
# x = np.random.rand(100,784)
# y=net.predict(x)
# print(y.shape)
# #计算梯度并将参数值保存
# t =np.random.rand(100,10)
# grads = net.numerical_gradient(x,t)
# print("计算梯度并保存")
# print(grads['W1'].shape)#(784, 100)
# print(grads['b1'].shape)#(100,)
# print(grads['W2'].shape)#(100, 10)
# print(grads['b2'].shape)#(10,)

from mnist import load_mnist   #minst数据集需要自己下载哦
#通过mini-batch实现
(x_train,t_train),(x_test,t_test) = load_mnist(normalize=True,one_hot_label=True)
train_loss_list = []
#超参数
iters_num  = 10000
train_size = x_train.shape[0] #60000个
batch_size = 100 #100为1捆
learning_rate = 0.1 #学习率
network = TwoLayerNet(input_size=784,hidden_size=50,output_size=10)
#初始化神经元,输入层784个神经元,隐藏层50个,输出层10个
for i in range(iters_num):
    #获取mini-batch
    #循环10000次(代表梯度法的更新次数)
    batch_mask = np.random.choice(train_size,batch_size)#从全部数据中选取100个,这100个是所有数据的索引
    x_batch = x_train[batch_mask] #选取输入数据
    t_batch = t_train[batch_mask] #选取标签 one-hot形式
    #计算梯度
    grad = network.numerical_gradient(x_batch,t_batch)
    #更新参数
    for key in('W1','b1','W2','b2'):
        network.params[key] -= learning_rate*grad[key]
    #记录学习过程,loss返回的是交叉熵的值
    loss = network.loss(x_batch,t_batch)
    print(loss)
    train_loss_list.append(loss)


  • 8
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值