现在我们来搭建一个两层的神经网络

import sys,os
sys.path.append(os.pardir)
from common.functions import *
from common.gradient import numerical_gradient
from dataset.mnist import  load_mnist


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)

    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):
        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(input_size=784,hidden_size=100,output_size=10)

(x_train,t_train),(x_test,t_test) = load_mnist(normalize = True,one_hot_label=True)
# 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(net.numerical_gradient(x_train,t_train))

个人感觉loss 还不是很完善,

print(net.numerical_gradient(x_train,t_train))

传入训练数据和标签,求的是网络的梯度。

 grads['W1'] = numerical_gradient(loss_W,self.params['W1'])

上面这个代码的 numerical_gradient 是在另一个文件里定义的。

定义如下:

def numerical_gradient(f, x):  # f是loss,x是权重w
    h = 1e-4 # 0.0001
    grad = np.zeros_like(x)
    
    it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])
    while not it.finished:
        idx = it.multi_index
        tmp_val = x[idx]
        x[idx] = float(tmp_val) + h  #在这里 w 被修改了,x 就是w

        fxh1 = f(x) # f(x+h)   ,
        # 这里 f(W)其实就是 loss(x,y)
        #在这里会改变函数外参数的值
        # loss(x,y)里面有个predict 让修改的w和x相乘
        x[idx] = tmp_val - h 
        fxh2 = f(x) # f(x-h)
        grad[idx] = (fxh1 - fxh2) / (2*h)
        
        x[idx] = tmp_val # 还原值
        it.iternext()   
        
    return grad

这个时候发现问题了:

 grads['W1'] = numerical_gradient(loss_W,self.params['W1'])

loss_W 是f,self.params['W1']是 x

然后求 f(x),也就是 loss_W(x),  但是

loss_W 需要两个参数,而 只传入一个self.params['W1'] 。

f = lambda w: net.loss(x, t)  # 定义函数f
等价于
def f(w):
	return net.loss(x, t)

再顺一遍发现,求的梯度是  x  和  t  的差距 

w和损失函数只有间接关系。

参考了:(19条消息) 解析深度学习入门代码f = lambda w: net.loss(x, t)_嘻嘻作者哈哈的博客-CSDN博客https://blog.csdn.net/weixin_43971252/article/details/109066536

但是他说每次predict用的是修改过的w,但是这个代码里,没看到W修改 

 x[idx] = float(tmp_val) + h  #在这里 w 被修改了,而且函数外的w也被修改了
 fxh1 = f(x) # f(x+h)   ,
        # 这里 f(W)其实就是 loss(x,y)
        #当传入的是字典型,列表型时如果是重新对其赋值则不会改变函数外参数的值,如果是进行操作,则会改变
        #https://blog.csdn.net/liuxiao214/article/details/81673093
        # loss(x,y)里面有个predict  。它让修改的w和x相乘

至于w是怎么修改的,建议看下面完整版,代码。因为没看给的源码,走了好多弯路,(有更新的语句,书上在前面提到了,但是写这个类的时候没给出来,,浪费一天看这个。。。拉跨)


# 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 = 10000  # 适当设定循环的次数
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)

for i in range(iters_num):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    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:
        train_acc = network.accuracy(x_train, t_train)
        test_acc = network.accuracy(x_test, t_test)
        train_acc_list.append(train_acc)
        test_acc_list.append(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()


关于python函数内改变参数的值  是否会影响到函数外的参数


def change(x):
    x[0] = x[0]+1

def change2(x):
    x['W'] = x['W']+1

def change3(x):
    x['W'] = 5

a = [0,1,2]
print(a)
change(a)
print(a)
'''
[0, 1, 2]
[1, 1, 2]

'''

b = {'W':2,'b':3}
print(b)
change2(b)
print(b)

'''
{'W': 2, 'b': 3}
{'W': 3, 'b': 3}
'''

c = {'W':3,'b':3}
print(c)
change3(c)
print(c)
'''
{'W': 3, 'b': 3}
{'W': 5, 'b': 3}
'''

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐