现在我们来搭建一个两层的神经网络
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和损失函数只有间接关系。
但是他说每次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}
'''