文章目录
神经网络由多个神经元连接而形成的网络,一个神经元可以看做一个简单的神经网络。神经元也称感知器,具有一定的拟合和分类的功能。 如果我们想要得到一个网络的感知器,我们需要先定义激活函数,然后求的最合适的w,b(权重和偏置)。
所以本章将通过三个方面来对感知器进行阐述。
1.介绍感知器;2.介绍激活函数;3.求wb
1. 感知器定义
一个感知器可以理解成有多个线性拟合组合成的计算单元,由不同的输入特征(x)依据不同的权重(w)及偏置项(b)加权求和得到了激活函数(σ),如图。
首先输入的m个特征x1到xm经过不同的权重拟合,经过加权求和,加偏置项,激活函数变换后得到了我们需要的结果y,所以只要我们确定了w和b(权重和偏置项)我们的感知器基本就是确定的。
2. 激活函数
2.1 常用的激活函数
这里介绍三种常用的激活函数,sigmoid(),tanh(),relu()
(1)三种激活函数的值域比较
由图可知,,sigmoid函数的值域是在(0,1)之间的,他可以将(-∞,+∞)的x值映射到值域区间内,Tanh函数的值域是(-1,1),ReLU函数的值域是(0,+∞)。由于sigmoid和tanh都是对x的非线性变换(不满足y=ax+b),所以他们多用于全连接网络,实现网络的非线性拟合功能。而ReLU函数多用于卷积神经网络。
(2)三种函数对于定义域比较
我们不难发现,sigmoid函数和Tanh函数对于x的两端无穷都是无限逼近某个实数(1,-1和0),这表明,这两种函数对于x附近的值有着很强的处理区分能力,但是对于远离0点的值,处理能力很弱。所以当感知器使用这两种激活函数时**,我们要尽量对输入数据进行归一化,**如果要是进行拟合问题的时候,我们同样要对y值进行归一化。
与此同时,我们还要使用偏置项b进行函数曲线水平方向移动,尽量移动到0附近。
以下是绘制三种激活函数的python代码,可供参考。
import numpy as np
import matplotlib.pyplot as plt
# 定义 Sigmoid 函数
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# 定义 Tanh 函数
def tanh(x):
return np.tanh(x)
# 定义 ReLU 函数
def relu(x):
return np.maximum(0, x)
# 生成输入数据
x = np.linspace(-5, 5, 100)
# 计算各函数的输出
sigmoid_y = sigmoid(x)
tanh_y = tanh(x)
relu_y = relu(x)
# 绘图
plt.figure(figsize=(12, 6))
plt.subplot(1, 3, 1)
plt.plot(x, sigmoid_y, label='Sigmoid', color='blue')
plt.title('Sigmoid Function')
plt.xlabel('x')
plt.ylabel('Sigmoid(x)')
plt.legend()
plt.subplot(1, 3, 2)
plt.plot(x, tanh_y, label='Tanh', color='red')
plt.title('Tanh Function')
plt.xlabel('x')
plt.ylabel('Tanh(x)')
plt.legend()
plt.subplot(1, 3, 3)
plt.plot(x, relu_y, label='ReLU', color='green')
plt.title('ReLU Function')
plt.xlabel('x')
plt.ylabel('ReLU(x)')
plt.legend()
plt.tight_layout()
plt.show()
(3)PyTorch中的三种激活函数代码
# x必须是张量
torch.sigmoid(x)
torch.tanh(x)
torch.relu(x)
3 求最优权重和偏置项(w,b)的方法
我们的目标是,对于任意输入的(x,y),我们希望我们感知器所输出的y1与原本输入的y(我们所期望的)尽可能一致,因此我们构造一种非负的函数来表示这种接近程度,如果感知器生成的y1与真实y相近,则函数值越小(但大于0),当两者完全一样的时候,函数值达到最小,将这个函数记作L(y1,y),其中y1=σ(wx+b)。
我们所需要做的就是确定w,b使得y1最小。我们将y1函数称为目标函数,而在PyTorch中,我们经常将目标函数称为损失函数。
对于回归问题我们经常使用的目标函数为:
L
(
y
1
,
y
)
=
1
2
(
y
1
−
y
)
2
L(y_1,y) = \frac{1}{2} (y_1-y)^2
L(y1,y)=21(y1−y)2
对于分类问题(一个感知器只能解决一个二分类问题,所以此处我们代指二分类)我们经常用的目标函数为:
L
(
y
1
,
y
)
=
−
y
l
o
g
(
y
1
)
−
(
1
−
y
)
l
o
g
(
1
−
y
1
)
L(y_1,y) = -ylog(y_1)-(1-y)log(1-y_1)
L(y1,y)=−ylog(y1)−(1−y)log(1−y1)
接下来我们介绍随机梯度下降算法(3.1-3.3是回归问题,3.4是二分类问题)
3.1梯度下降算法(一元函数)
每次沿着梯度下降最快的方向(与梯度相反的方向=该点导数相反的方向)更新参数,从而快速的找到所需要的参数。更新参数时,选择用学习率lr来控制步长,通常学习率需要手工设置,0.1,0.01等非负小实数。从本质上来讲,我们调整的部分为(学习率*该点导数)
实例
给定函数,用梯度下降的方法求最小值(PyTorch)
f
(
w
)
=
2
(
w
−
1
)
2
+
1
f(w) =2(w-1)^2+1
f(w)=2(w−1)2+1
import torch
def f(w):#定义原函数
t = 2*(w-2)**2+1
return t
def df(w):#定义原函数导数
t = 4(w-2)
return t
lr = 0.1#定义学习率(步长的系数)
w = torch.Tensor([5.0])#定义起点
for epoch in range(20):#开始循环
w = w-lr*df(w)
y = f(w)
w,y = round(w,item(),2),round(y,item(),2)
print("最小值为(%0.2f,%0.2f)"%(w,y))
3.2随机梯度下降算法(多元函数,单个样本)
如前所述,我们的网络结构是由m个w加上1个b所构成的,所以我们一共有(m+1)元,我们需要对(m+1)个参数进行优化。当我们对某一个参数进行优化的时候,我们将其它参数看成是常数。如果每次更新的时候只利用一个样本进行梯度计算,以此更新参数,那么这种梯度下降的算法叫做梯度下降算法。
步骤如下:
输入:数据集,未知的wx+b
输出:训练好的wx+b
1.读取数据集,设置学习率(lr)
2.随机初始化w,b
3.设计目标函数及导数函数
4.随机打乱样本顺序,进行循环,求每一个w和b
5.输出wx+b
实例
对一个平面上的10个坐标点(1,-9.51)(2,-5.74)(3,-2.84)(4,-1.80)(5,0.54)(6,1.51)(7,4.33) (8,7.06)(9,9.34)(10,10.72)构造一个感知器,对离散点进行拟合。(只有一个特征x)
import torch
import matplotlib.pyplot as plt
#1.数据读取,定义感知器函数未知的wx+b,设置学习率
x = [1,2,3,4,5,6,7,8,9,10]
y = [-9.51,-5.74,-2.84,-1.80,0.54,1.51,4.33,7.06,9.34,10.72]
X = torch.Tensor(X)
Y = torch.Tensor(Y)
lr = torch.Tensor([0.01])
def f(x):
t = w*x+b
return t
#2.随机初始化w,b
w,b = torch.rand(1),torch.rand(1)
#3.设计导数函数(因为我们需要利用的是导数,所以不用显式计算原函数)
def dw(x,y):
t = (f(x)-y)*x
return t
def db(x,y):
t = (f(x)-y)
return t
#4.进行循环,求每一个w和b
for epoch in range(100):
for x,y in zip(X,Y):
dw_v,db_v = dw(x,y),db(x,y)
w = w-lr*dw_v
b = b-lr*db_v
#5.输出绘制感知器函数直线图
plt.scatter(X,Y,c='r')
X2=[x[0],X[len(X)-1]]
Y2=[f(x[0]),f(X[len(X)-1])]
plt.plot(X2,Y2,'--',c='b')
plt.tick_params(labelsize=13)
plt.show()
3.3 批量梯度下降算法(多个样本)
当输入多个样本后,利用多个样本的平均误差做一次梯度计算和参数更新的方法叫做批量梯度下降算法。梯度下降算法是先计算n个样本的误差平均值,然后通过梯度计算对w,b进行一次更新。
实例:
对一个平面上的10个坐标点(1,-9.51)(2,-5.74)(3,-2.84)(4,-1.80)(5,0.54)(6,1.51)(7,4.33) (8,7.06)(9,9.34)(10,10.72)构造一个感知器,对离散点进行拟合。要求使用批量梯度下降算法,其中批的大小设置为4。
import torch
import matplotlib.pyplot as plt
#1.数据读取,定义感知器函数未知的wx+b,设置学习率
torch.manual_seed(123)
x = [1,2,3,4,5,6,7,8,9,10]
y = [-9.51,-5.74,-2.84,-1.80,0.54,1.51,4.33,7.06,9.34,10.72]
X = torch.Tensor(X)
Y = torch.Tensor(Y)
#数据打包
n = 4
X1,Y1 = X[0:n],y[0:n]
X2,Y2 = X[n:2*n],y[n:2*n]
X3,Y3 = X[2*n:3*n],y[2*n:3*n]
X,Y = [X1,X2,X3],[Y1,Y2,Y3]
def f(x):
t = w*x+b
return t
def dw(x,y):
t = (f(x)-y)*x
return t
def db(x,y):
t = (f(x)-y)
return t
w,b = torch.rand(1),torch.rand(1)
lr = torch.Tensor([0.01])
#4.进行循环,求每一个w和b
for epoch in range(100):
for bX,bY in zip(X,Y):
dw_v,db_v = dw(bX,bY),db(bX,bY)
dw_v = dw_v.mean()
db_v = db_v.mean()
w = w-lr*dw_v
b = b-lr*db_v
print("优化后的w和b分别是%0.4f和%0.4f"%(w,b))
3.4二分类感知器构造
感知器在线性回归(只有一个实数值输出)的基础上,运用了激活函数sigmoid(),是的感知器的输出为0,1.
实例
在一个二维平面中,对给定的若干个线性的可分的离散点,其中部分属于0类,另一部分属于1类。离散点为(2.49,2.86)(0.50,0.21)(2.73,2.91)(3.47,2.34)(1.38,0.37)(1.03,0.27)(0.59,1.73)(2.25,3.75)(0.15,1.45)(2.73,3.42)分别标记为1,0,1,1,0,0,0,1,0,1。(样本的特征值个数为2个)
我们构造的感知器为:
y
1
=
s
i
g
m
o
i
d
(
w
∗
x
+
b
)
y_1= sigmoid(w*x+b)
y1=sigmoid(w∗x+b)
我们构造的感知器额目标函数为:
L
(
y
1
,
y
)
=
−
y
l
o
g
(
y
1
)
−
(
1
−
y
)
l
o
g
(
1
−
y
1
)
L(y_1,y) = -ylog(y_1)-(1-y)log(1-y_1)
L(y1,y)=−ylog(y1)−(1−y)log(1−y1)
import torch
import matplotlib.pyplot as plt
#读数据
X1 = [2.49,0.50,2.73,3.47,1.38,1.03,0.59,2.25,0.15,2.73]
X2 = [2.86,0.21,2.91,2.34,0.37,0.27,1.73,3.75,1.45,3.42]
Y = [1,0,1,1,0,0,0,1,0,1]
X1 = torch.Tensor(X1)
X2 = torch.Tensor(X2)
X = torch.stack((X1,X2),dim=1)
Y = torch.Tensor(Y)
lr = torch.Tensor([0.1])
class Perceptron2():
def__int__(self):
self.w1 = torch.Tensor([0.0])
self.w2 = torch.Tensor([0.0])
self.b = torch.Tensor([0.0])
def f(self,x):
x1,x2 = x[0],x[1]
t = self.w1*x1+self.w2*x2+self.b
z = 1.0/(1+torch.exp(t))
return z
def forward_compute(self,x):
pre_y = self.f(x)
return pre_y
perceptron2 = Perceptron2()
for ep in range(100):
for (x,y) in zip(X,Y):
pre_y = perceptron2.forward_compute(x)
x1,x2 = x[0],x[1]
dw1 = ((1-y)*pre_y-(1-pre_y)*y)*x1
dw2 = ((1-y)*pre_y-(1-pre_y)*y)*x2
db = ((1-y)*pre_y-(1-pre_y)*y)*1
perceptron2.w1 = perceptron2.w1+lr*dw1
perceptron2.w2 = perceptron2.w2+lr*dw2
perceptron2.b = perceptron2.b+lr*db
s = '学习到的感知器:pre_y = sigmoid(%0.2f*x1+%0.2f*x2+%0.2f)'%(perceptron2.w1,perceptron2.w2,perceptron2.b)
print(s)
for (x,y) in zip(X,Y):
t = 1 if perceptron2.f(x)>0.5 else 0
s=''
if t == y.item():
s='(点%0.2f,%0.2f被正确分类)'%(x[0],x[1])
else:
s='(点%0.2f,%0.2f被错误分类)'%(x[0],x[1])
print(s)
以上,本文内容参考自蒙祖强,欧元汉编著的《深度学习理论与应用》。