一、单层感知器(二分类问题)

1.原理及其理论

详细原理可以参考CSDN专栏https://blog.csdn.net/pxhdky/article/details/86360535,博主为齐在
这个原理是感知器的原理,与神经网络的感知机原理有些出入,两者可以对比来看
关于神经网络中单层感知机原理,可以参考专栏https://www.cnblogs.com/pythonlearing/p/9947051.html,博主为小纯洁的兄弟

2.代码实现

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. #输入数据
  4. X = np.array([[1,3,3],
  5. [1,4,3],
  6. [1,1,1],
  7. [1,0,2]]) #第一个值为偏置值,恒为1.要习惯写二维数组的形式!shape为4*3
  8. #标签
  9. Y = np.array([[1],
  10. [1],
  11. [-1],
  12. [-1]])
  13. #权值初始化,3行1列,取值范围-1到1
  14. W = (np.random.random([3,1])-0.5)*2 #本来生成0~1之间的值,这里进行转化成生成-1到1的值
  15. print(W) #查看初始值
  16. #学习率设置
  17. lr = 0.11
  18. #神经网络输出
  19. O = 0
  20. def update(): #更新权值
  21. global X,Y,W,lr #在函数内部对函数外部进行操作,需要用global声明
  22. O = np.sign(np.dot(X,W)) # shape:(3,1),np.dot为矩阵乘法,这里也可以表示为np.dot(W.T,X)
  23. W_C = lr*(X.T.dot(Y-O))/int(X.shape[0]) #delta wi = lr*(t-y)xi,这里t-y是(Y-O),X.T是xi,这里除以int是因为要求平均的误差,数据量比较小的情况下可以不用除,数据量较大时会导致权值调整过大而出现误差
  24. W = W + W_C
  25. for i in range(100):
  26. update()#更新权值
  27. print(W)#打印当前权值
  28. print(i)#打印迭代次数
  29. O = np.sign(np.dot(X,W))#计算当前输出
  30. if(O == Y).all(): #如果实际输出等于期望输出,模型收敛,循环结束
  31. print('Finished')
  32. print('epoch:',i)
  33. break
  34. #画图
  35. #正样本
  36. x1 = [3,4] #定义X1
  37. y1 = [3,3] #定义X2
  38. #负样本
  39. x2 = [1,0]
  40. y2 = [1,2]
  41. #计算分界线的斜率以及截距
  42. k = -W[1]/W[2] #w0 + w1x1 + w2x2 = 0
  43. d = -W[0]/W[2]
  44. print('k=',k)
  45. print('d=',d)
  46. xdata = (0,5)
  47. plt.figure()
  48. plt.plot(xdata,xdata*k+d,'r')
  49. plt.scatter(x1,y1,c='b')
  50. plt.scatter(x2,y2,c='y')
  51. plt.show() #初始化w值不同,会出现不同的线

3.单层感知机的异或问题

单层感知机一个缺陷依赖于初始权值,不同的权值会导致不同的超平面,有些超平面会导致最后分类的效果比较差;还有一个缺陷不能解决非线性的问题
一般单层感知机的异或问题是非线性问题,比如上面的程序,把数据集改为
X = np.array([[1,0,0],
[1,0,1],
[1,1,0],
[1,1,1]])
此时没有一个线性超平面把点准确分开,会出现迭代多次后仍没有最优解,这是单层感知机不能做到的

二、线性神经网络

1.原理及其理论

线性神经网络的原理思想可以参考CSDN专栏https://blog.csdn.net/jerry_liufeng/article/details/107886872,作者为jerry_liufeng

Delta学习规则:线性神经网络的一种基于梯度下降法的学习规则
这里的损失函数为二次代价函数:E = 1/2(t-y) = 1/2[t - f(WX)],f(WX)是激活函数(不一定是哪种形式)E是权向量W的函数,可以使用梯度下降法进行:
神经网络(python实现) - 图1
神经网络(python实现) - 图2,δ是一个符号,代表了(t-y)f(WX)
线性神经网络可以解决异或性,第一个方法是用多个线性函数对区域进行划分,第二个方法是添加非线性输入,从而引入非线性成分

2.代码实现

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. #输入数据
  4. X = np.array([[1,3,3],
  5. [1,4,3],
  6. [1,1,1],
  7. [1,0,2]]) #X是4*3的
  8. #标签
  9. Y = np.array([[1],
  10. [1],
  11. [-1],
  12. [-1]])
  13. #权值初始化,3行1列,取值范围-1到1
  14. W = (np.random.random([3,1])-0.5)*2
  15. print(W)
  16. #学习率设置
  17. lr = 0.11
  18. #神经网络输出
  19. O = 0
  20. def update():
  21. global X,Y,W,lr
  22. O = np.dot(X,W) #此时没有激活函数sign,而是利用新的激活函数y = x,O是4*1维的
  23. W_C = lr*(X.T.dot(Y-O))/int(X.shape[0]) #W是3*1的,X.T是3*4的,Y-O是4*1的,乘完之后是3*1的
  24. W = W + W_C #更新权值
  25. #画图求解
  26. for _ in range(100):
  27. update()#更新权值
  28. print(W)#打印当前权值
  29. print(i)#打印迭代次数
  30. O = np.sign(np.dot(X,W))#计算当前输出
  31. if(O == Y).all(): #如果实际输出等于期望输出,模型收敛,循环结束
  32. print('Finished')
  33. print('epoch:',i)
  34. break
  35. #正样本
  36. x1 = [3,4]
  37. y1 = [3,3]
  38. #负样本
  39. x2 = [1,0]
  40. y2 = [1,2]
  41. #计算分界线的斜率以及截距
  42. k = -W[1]/W[2]
  43. d = -W[0]/W[2]
  44. print('k=',k)
  45. print('d=',d)
  46. xdata = (0,5)
  47. plt.figure()
  48. plt.plot(xdata,xdata*k+d,'r')
  49. plt.scatter(x1,y1,c='b')
  50. plt.scatter(x2,y2,c='y')
  51. plt.show()

3.线性神经网络解决异或问题

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. #输入数据
  4. X = np.array([[1,0,0,0,0,0],
  5. [1,0,1,0,0,1],
  6. [1,1,0,1,0,0],
  7. [1,1,1,1,1,1]])#x0全为1,x1为(0,0,1,1)',x2为(0,1,0,1)'第四列为x1平方,第五列是x1x2,第六列是x2平方(加入非线性项)
  8. #标签
  9. Y = np.array([[-1],
  10. [1],
  11. [1],
  12. [-1]]) #y为4*1维
  13. #权值初始化,3行1列,取值范围-1到1
  14. W = (np.random.random([6,1])-0.5)*2 #W为6*1维
  15. print(W)
  16. #学习率设置
  17. lr = 0.11
  18. #神经网络输出
  19. O = 0
  20. def update():
  21. global X,Y,W,lr
  22. O = np.dot(X,W) #此时没有激活函数sign,而是使用y = x
  23. W_C = lr*(X.T.dot(Y-O))/int(X.shape[0]) #这里的一定不要搞错,否则后面很容易出问题!
  24. W = W + W_C
  25. for _ in range(100):
  26. update()#更新权值
  27. #正样本
  28. x1 = [0,1]
  29. y1 = [1,0]
  30. #负样本
  31. x2 = [0,1]
  32. y2 = [0,1]
  33. def calculate(x,root):#这里是计算x0w0 + x1w1 +x2w2 + x1^2w3 + x1x2w4 +x2^2w5 = 0的根情况,这里最终计算x2的根的情况
  34. a = W[5]
  35. b = W[2]+x*W[4]
  36. c = W[0]+x*W[1]+x*x*W[3]
  37. if root == 1:
  38. return(-b+np.sqrt(b*b-4*a*c))/(2*a)
  39. if root == 2:
  40. return(-b-np.sqrt(b*b-4*a*c))/(2*a)
  41. xdata =np.linspace(-1,2) # linspace是线性划分区间,生成一系列的点
  42. plt.figure()
  43. plt.plot(xdata,calculate(xdata,1),'r') #生成两条边界线,达成线性神经网络可以实现解决异或问题
  44. plt.plot(xdata,calculate(xdata,2),'r') #生成两条边界线,达成线性神经网络可以实现解决异或问题
  45. plt.scatter(x1,y1,c='b')
  46. plt.scatter(x2,y2,c='y')
  47. plt.show()

三、BP神经网络(重点)

基本的算法原理比较复杂,可以参考周志华的西瓜书,这里只给出最后的结果形式,可以参照简书专栏https://www.jianshu.com/p/7f1cb367a852的图例帮助理解BP神经网络的过程,作者为暑水。
CSDN专栏有专业的数学推导和最终形式,可以参考https://blog.csdn.net/u014303046/article/details/78200010,博主为痴澳超

1.代码实例实现——解决异或问题

首先要有一个构建神经网络的思路,这里以简单的三层感知器为例,假设输入层有三个值x0,x1,x2,(其中x0恒为1为偏置值),隐藏层有一层,包含四个神经元y0,y1,y2,y3,输出层有一个神经元O
设V00,V01,V02,V03是输入层X0到隐藏层的权值,V10,V11,V12,V13是输入层X1到隐藏层的权值,V20,V21,V22,V23是输入层X2到隐藏层的权值;W0,W1,W2,W3是隐藏层到输出层的权值,于是有:
神经网络(python实现) - 图3 = 神经网络(python实现) - 图4神经网络(python实现) - 图5
神经网络(python实现) - 图6神经网络(python实现) - 图7 = O

  1. import numpy as np
  2. #输入数据
  3. X = np.array([[1,0,0],
  4. [1,0,1],
  5. [1,1,0],
  6. [1,1,1]]) #X是4*3的
  7. #标签
  8. Y = np.array([[0,1,1,0]])
  9. #权值初始化,取值范围-1到1
  10. V = np.random.random((3,4))*2-1 #这里假设输入值有x0,x1,x2三个,隐藏层只有一层,且隐藏层的神经元有4个,输出值只有一个,那么在输入层和隐藏层之间有3*4维的权值
  11. W = np.random.random((4,1))*2-1 #这里是隐藏层和输出层之间的权值,为4*1维
  12. print(V)
  13. print(W)
  14. #学习率设置
  15. lr = 0.11
  16. def sigmoid(x): #定义激活函数sigmoid函数
  17. return 1/(1+np.exp(-x))
  18. def dsigmoid(x): #定义激活函数的求导形式
  19. return x*(1-x)
  20. def update(): #更新权值
  21. global X,Y,W,V,lr
  22. L1 = sigmoid(np.dot(X,V))#隐藏层输出(4,4) (y0,y1,y2,y3)
  23. L2 = sigmoid(np.dot(L1,W))#输出层输出(4,1) (O)
  24. L2_delta = (Y.T - L2)*dsigmoid(L2) #4*1维;权值更新公式,由于是反向先更新L2_delta:delta = (dk - ok)ok(1-ok),其中dk是期望输出对应Y,ok是实际运算得到的输出
  25. L1_delta = L2_delta.dot(W.T)*dsigmoid(L1) #4*4维;权值更新公式,L1_delta = L2_delta*W*yj(1-yj),这里L2_delta*W在多层下是加权平均,在这里只是单层,后面是激活函数的导数L1
  26. W_C = lr*L1.T.dot(L2_delta) #权值调整公式,为lr*L2_Delta*yj(隐藏层输出)
  27. V_C = lr*X.T.dot(L1_delta) #权值调整公式,为lr*L1_Delta*xi(输入层输出)
  28. W = W + W_C
  29. V = V + V_C
  30. for i in range(20000): #进行迭代
  31. update()#更新权值
  32. if i%500==0: #每隔500次输出一次误差变化
  33. L1 = sigmoid(np.dot(X,V))#隐藏层输出(4,4)
  34. L2 = sigmoid(np.dot(L1,W))#输出层输出(4,1)
  35. print('Error:',np.mean(np.abs(Y.T-L2)))
  36. L1 = sigmoid(np.dot(X,V))#隐藏层输出(4,4)
  37. L2 = sigmoid(np.dot(L1,W))#输出层输出(4,1)
  38. print(L2) #循环结束后输出L2情况
  39. def judge(x):
  40. if x>=0.5:
  41. return 1
  42. else:
  43. return 0
  44. for i in map(judge,L2): #map() 会根据提供的函数对指定序列做映射。第一个参数 function 以参数序列中的每一个元素调用 function 函数,返回包含每次 function 函数返回值的新列表。
  45. print(i)