机器学习小白修炼之路
深度学习的基础BP算法
神经元模型
单个神经元就是一个最简单的逻辑回归,只能解决比较简单的classification。
多个神经元可以解决一下较为复杂的分类问题。
前向传播
特征向量x从输入层到最后输出层的过程。
前向传播代码实现(估计是最后次发Matlab代码了,python真香警告!!!):
......
z1=theta1*d_x;
for i=1:5,
%Matlab构造的向量默认是行向量
hidden1(i)=sigmoid(z1(i));
end
z2=theta2*hidden1';
for i=1:5,
hidden2(i)=sigmoid(z2(i));
end
z3=theta3*hidden2';
output=sigmoid(z3);
......
上面的d_x向量是列向量。
误差逆传播(BP算法的精华部分)
将误差从输出层返传回紧挨着输入层的隐藏层的过程。返回的过程主要是修改每一层每个连接的权重,达到减少误差的过程。
反向传播代码实现(吴恩达 说的向量化代码也打了注释了,ps:向量化用时少,但是真的不好想)
......
%反向传播
%output层的error(损失函数对Z求偏导)计算方法为直接对Z求偏导,链式法则链到a
error3=(output-y(num))*(1-output)*output;
%error2=error3*theta3.*hidden2(i).*(1-hidden2(i));
for i=1:5,
error2(i)=error3*theta3(:,i)*hidden2(i)*(1-hidden2(i));
end
%error1=error2*theta2.*hidden1(i).*(1-hidden1(i));
for i=1:5,
error1(i)=error2*theta2(:,i)*hidden1(i)*(1-hidden1(i));
end
%更新权重
%theta1=theta1-interval*error1'*d_x';
for i=1:5,
for j=1:3,
theta1(i,j)=theta1(i,j)+interval*error1(i)*x(j);
end
end
%theta2=theta2-interval*error2'*hidden1;
for i=1:5,
for j=1:5,
theta2(i,j)=theta2(i,j)+interval*error2(i)*hidden1(j);
end
end
%theta3=theta3-interval*error3'*hidden2;
for i=1:5,
theta3(i)=theta3(i)+interval*error3*hidden2(i);
end
......
诀窍:(大前提:w1的dimension=[inputsize,hiddensize]…)
从输出层计算error,公式记作:下一层error点乘这一层权重的转置再乘上这层输出函数的导数。(ps:sigmoid函数在a的导数等于sigmoid(a)*(1-sigmoid(a)))
partical J除以partical w(某一层)等于上一层输出的转置点乘这层的error。
完整代码
Matlab
function [ ] = build_bp( )
INTERVAL=0.8;
x=load('D:\machineLearning\逻辑回归\ex4Data\ex4x.dat');
y=load('D:\machineLearning\逻辑回归\ex4Data\ex4y.dat');
m=length(x(:,1));
c_p=find(y==1);
s_p=find(y==0);
x(:,1)=(x(:,1)-mean(x(:,1)))./std(x(:,1))
x(:,2)=(x(:,2)-mean(x(:,2)))./std(x(:,2))
%plot(x(c_p,1),x(c_p,2),'ro');
%hold on;
%plot(x(s_p,1),x(s_p,2),'bs');
x=[ones(m,1) x];
%搭建三层神经网络,需要三个权重矩阵
theta1=rand(5,3)*2*INTERVAL-INTERVAL;
theta2=rand(5,5)*2*INTERVAL-INTERVAL;
theta3=rand(1,5)*2*INTERVAL-INTERVAL;
interval=0.4;
iter=100;
c=1;
loss=0;
while c<=iter,
t=0;
for num=1:80,
transpose_x=x';
d_x=transpose_x(:,num);
d_y=y;
%前向传播
z1=theta1*d_x;
for i=1:5,
%Matlab构造的向量默认是行向量
hidden1(i)=sigmoid(z1(i));
end
z2=theta2*hidden1';
for i=1:5,
hidden2(i)=sigmoid(z2(i));
end
z3=theta3*hidden2';
output=sigmoid(z3);
%反向传播
%output层的error(损失函数对Z求偏导)计算方法为直接对Z求偏导,链式法则链到a
error3=(output-y(num))*(1-output)*output;
%error2=error3*theta3.*hidden2(i).*(1-hidden2(i));
for i=1:5,
error2(i)=error3*theta3(:,i)*hidden2(i)*(1-hidden2(i));
end
%error1=error2*theta2.*hidden1(i).*(1-hidden1(i));
for i=1:5,
error1(i)=error2*theta2(:,i)*hidden1(i)*(1-hidden1(i));
end
%更新权重
%theta1=theta1-interval*error1'*d_x';
for i=1:5,
for j=1:3,
theta1(i,j)=theta1(i,j)+interval*error1(i)*x(j);
end
end
%theta2=theta2-interval*error2'*hidden1;
for i=1:5,
for j=1:5,
theta2(i,j)=theta2(i,j)+interval*error2(i)*hidden1(j);
end
end
%theta3=theta3-interval*error3'*hidden2;
for i=1:5,
theta3(i)=theta3(i)+interval*error3*hidden2(i);
end
e=(output-y(num))^2/2; %计算误差向量 (计算输出-目标输出)
t=t+e;
end
loss(c)=t;
c=c+1;
end
plot(1:iter,loss);
end
图像
python
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
class Neural_Network(object):
def __init__(self):
self.inputSize=2
self.outputSize=1
self.hiddenSize = 3
self.alpha=0.2
self.iterations=100
self.count_error=[]
self.w1=np.random.randn(self.inputSize,self.hiddenSize)
self.w2=np.random.randn(self.hiddenSize,self.hiddenSize)
self.w3=np.random.randn(self.hiddenSize, self.outputSize)
def sigmoid(self,s):
return 1/(1+np.exp(-s))
def forward(self,x,y):
self.z1=np.dot(x,self.w1)
self.hidden1=self.sigmoid(self.z1)
self.z2=np.dot(self.hidden1,self.w2)
self.hidden2=self.sigmoid(self.z2)
self.z3=np.dot(self.hidden2,self.w3)
self.output=self.sigmoid(self.z3)
self.count_error.append(np.mean(np.square(y-self.output)))
return self.output
def backward(self,x,y,output):
#error2表示损失值对Z求偏导数。链式法则:先对a求偏导,再乘以a对Z求偏导
self.error3=(output-y)*(1-output)*output
#链式法则,链接到下一层Z求解
self.error2=self.error3.dot(self.w3.T).dot(np.dot((1-self.hidden2),self.hidden2))
self.error1=self.error2.dot(self.w2.T).dot(np.dot((1-self.hidden1),self.hidden1))
self.w1-=self.alpha*x.T.dot(self.error1)
self.w2-=self.alpha*self.hidden1.T.dot(self.error2)
self.w3-=self.alpha*self.hidden2.T.dot(self.error3)
def train(self,x,y):
for i in range(self.iterations):
output=self.forward(x,y)
self.backward(x,y,output)
if __name__=="__main__":
x=np.array(([0.3,1],[0.2,0.5],[0.4,0.8]),dtype=float)
y=np.array(([0.9],[0.8],[0.7]),dtype=float)
#plt.scatter(x[:,0],x[:,1])
#plt.show()
NN=Neural_Network()
NN.train(x,y)
plt.plot(range(1,NN.iterations+1),NN.count_error)
plt.title('error')
plt.xlabel('iteration')
plt.ylabel('error')
plt.show()
图像
分析总结
手搓BP反向传播算法,其实Matlab的代码,写好晾了几乎有一礼拜了,之前一直觉得loss值有问题就一直在思考着:loss值振荡是什么鬼东西?其实说到底一个Neuron也就是简单的逻辑回归,逻辑回归的loss函数说到底是个交叉熵,如下图(参考自李宏毅老师的深度学习课程讲解):
如果你记录的loss值不是一轮训练完之后的平均值,而是整个数据集中的单个样本计算出来的loss值,那么有振荡现象是很正常的。当你的记录loss值是一轮训练完之后的平均值的时候,那loss函数的图像应该就会是正常缓慢下降的了。
现在深度学习框架中封装好的损失函数,一般也都是在一轮训练结束后取的平均误差。比如说均方误差,一听名字就知道了。
而且python喂数据的形式和matlab也不一样,二维张量是所有组一起喂到网络里面迭代的。如果在matlab的话会是一次进一组数去迭代,再下一轮进下一组数。
这么想也就明白了奇怪的图像了。