mse函数python mse函数和sse
这是一段计算图像信噪比的python代码,请问第三四行是什么意思
第三行是判断mse是不是小于1e-10(表示1*10^-10),如果是的话,函数直接返回100
成都创新互联服务项目包括麻山网站建设、麻山网站制作、麻山网页制作以及麻山网络营销策划等。多年来,我们专注于互联网行业,利用自身积累的技术优势、行业经验、深度合作伙伴关系等,向广大中小型企业、政府机构等提供互联网行业的解决方案,麻山网站推广取得了明显的社会效益与经济效益。目前,我们服务的客户以成都为中心已经辐射到麻山省份的部分城市,未来相信会继续扩大服务区域并继续获得客户的支持与信任!
pytorch全连接层计算时间短
1.读取数据
首先,引入必要的头文件,并从文件中读入数据:
import pandas as pd import numpy as np import matplotlib.pyplot as plt %matplotlib inline import torch import torch.nn as nn from torch.autograd import Variable df = pd.read_csv('data/AirPassengers.csv') plt.plot(df['#Passengers']) plt.show()
程序输出如下图所示:
2.归一化
无论机器学习还是深度学习,使用哪一种框架,归一化都是必要环节。归一化的目标是将每一维特征压缩到一定范围之内,以免不同特征因取值范围不同而影响其权重。非常大或非常小的值搭配上不恰当的学习率,往往使得收敛过慢,或者因每次调整的波动太大最终无法收敛。归一化去除了这些不稳定因素。
归一化的具体做法是将某一列特征转换成均值为 0、标准差为1的数据,在图像处理过程中,也常把0-255之间的颜色值转换为0-1之间的小数。
本例中使用了均值和标准差编写了归一化和反归一化函数:
def feature_normalize(data): mu = np.mean(data,axis=0) # 均值 std = np.std(data,axis=0) # 标准差 return (data - mu)/std def feature_unnormalize(data, arr): mu = np.mean(data,axis=0) std = np.std(data,axis=0) return arr * std + mu
3.提取新特征
提取新特征是指从现有特征中提取更多可以代入模型的信息,从而生成新特征,本例中的数据包括两列,第一列“Month”是字符串类型的时间,第二列“#Passengers”是乘客量,也就是需要预测的数据y。下面通过拆分和类型转换,从第一列中提取具体的年“year”和月“mon”,将索引列变为特征“x”,并使用上面定义的函数实现归一化功能。
df['year'] = df['Month'].apply(lambda x: float(x[:4])) df['mon'] = df['Month'].apply(lambda x: float(x[5:])) df['x'] = feature_normalize(df.index) df['y'] = feature_normalize(df['#Passengers']) df['year'] = feature_normalize(df['year']) df['mon'] = feature_normalize(df['mon']) df['real'] = feature_unnormalize(df['#Passengers'], df['y'])
处理后的数据如下图所示:
4.处理缺失值和异常值
处理缺失值和异常值也是特征工程的重要环节,有时花费的时间比建模还多。处理缺失值的常用方法是删除重要特征缺失的item,或者用均值,前后值填充;处理异常值是监测数据中不正常的值,并做出相应处理,由于本例中数据比较“干净”,无需做缺失值和异常值处理。
5.向量化
向量化是将读出的数据转换成模型需要的数据格式,根据不同的模型做法不同,本例中的向量化将在后面的模型部分实现。
6.切分训练集和测试集
训练前还需要把数据切分成训练集和测试集,以避免过拟合,本例中将70%的数据用于训练,最终模型将对所有数据预测并做图。
TRAIN_PERCENT = 0.7 train_size = int(len(df) * TRAIN_PERCENT) train = df[:train_size]
拟合直线
拟合程序分成三部分:定义模型、优化器和误差函数;训练模型;预测并做图。
1.定义模型、优化器、误差函数
模型继承自mm.Module,并实现了两个核心函数,init用于初始化模型结构,forward用于定义前向传播的过程。本例中实现了最为简单的模型,其中只包含一个全连接层,使用nn.Linear定义,torch.nn中定义了常用的网络层实现。
class LinearRegression(nn.Module): def __init__(self): super(LinearRegression, self).__init__() self.linear = nn.Linear(1, 1) # 输入和输出的维度都是1 def forward(self, x): x = self.linear(x) return x model = LinearRegression() criterion = nn.MSELoss() # 损失函数:均方误差 optimizer = torch.optim.SGD(model.parameters(), lr=0.001) # 优化算法:随机梯度下降
损失函数使用了均方误差 MSELoss,它计算的是预测值与真值之差平方的期望值,MSELoss也是回归中最常用的损失函数,torch.nn中实现了一些常用的损失函数,可以直接使用,
优化的目标是更好地更新参数,使模型快速收敛。优化算法就是调整模型参数更新的策略,优化器是优化算法的具体实现。本例中优化器optimizer使用了最基础的随机梯度下降optim.SGD优化方法,torch.optim中定义了常用的优化器。在参数中设置了学习率为0.001,并将模型的参数句柄传入优化器,优化器后期将调整这些参数。
注意:学习率是一个重要参数,最好从小到大设置,如果设置太大,可能造成每次对参数修改过大,造成抖动,使得最终无法收敛。
2.训练模型
训练之前,先把数据转换成模型需要的数据格式,将pandas的数据格式转换为float32格式的Tensor张量,然后用unsqueeze扩展维度到2维(unsqueeze已在上一篇详细介绍)。
x = torch.unsqueeze(torch.tensor(np.array(train['x']), dtype=torch.float32), dim=1) y = torch.unsqueeze(torch.tensor(np.array(train['y']), dtype=torch.float32), dim=1) for e in range(10000): inputs = Variable(x) target = Variable(y) out = model(inputs) # 前向传播 loss = criterion(out, target) # 计算误差 optimizer.zero_grad() # 梯度清零 loss.backward() # 后向传播 optimizer.step() # 调整参数 if (e+1) % 1000 == 0: # 每1000次迭代打印一次误差值 print('Epoch:{}, Loss:{:.5f}'.format(e+1, loss.item()))
后面的循环部分进行了10000次迭代,也就是说将所有数据放进模型训练了10000次,从而使模型收敛。每一次循环之中,将x,y分别转换成变量Variable格式。
然后进行前先传播,model(inputs)调用的是nn.Module 的call()函数(call是Python类中的一个特殊方法,如果类中定义了此方法,可以通过实例名加括号的方式调用该方法)父类的call()调用了前向函数forward()将数据传入层中处理。
接下来是误差函数和优化器配合调整模型参数,此处到底修改了哪些值,又是如何修改的,是最难理解的部分。先通过定义的误差函数计算误差,从loss值可以看到每一次迭代之后误差的情况。
下一步是优化器清零,调用优化器的zero_grad方法,清除了model.parameters中的梯度grad。
之后是反向传播,误差函数的backward,调用了torch.autograd.backward()函数,backward()是上面定义的forward()的反向过程,对每层每一个参数求导,并填充在model.parameters的grad中。
最后调用优化器的step方法(step的具体实现可参考torch源码中optim/sgd.py中的step函数),它使用model.parameters中的梯度grad和设置的学习率、动量等参数计算出model.parameters的新data值,形如:weight = weight - learning_rate * gradient。
可以说,最后几步都是针对model.parameters模型参数的修改。整个过程可以通过跟踪model.parameters的data和grad的内容变化来分析。方法如下:
for p in model.parameters(): print(p.data, p.grad)
也可以在程序中加入以下代码,用于跟踪后向传播的过程:
f = loss.grad_fn while True: print(f) if len(f.next_functions) == 0: break f = f.next_functions[0][0]
3.预测和做图
本例中用70%数据作为训练集,用所有数据作为测试集,因此,用全部数据重新计算了x,y值;使用eval函数将模型转换为测试模式(有一些层在训练模型和预测模型时有差别);将数据代入模型预测,并转换成numpy格式作图显示。
x = torch.unsqueeze(torch.tensor(np.array(df['x']), dtype=torch.float32), dim=1) y = torch.unsqueeze(torch.tensor(np.array(df['y']), dtype=torch.float32), dim=1) model.eval() #将模型变为测试模式 predict = model(Variable(x)) # 预测 predict = predict.data.numpy() # 转换成numpy格式 plt.plot(x.numpy(), y.numpy(), 'y') plt.plot(x.numpy(), predict) plt.show()
程序运行结果如下图所示,可以看到模型用一条直线拟合曲线,在前70%的训练数据中表现更好。
多特征拟合
直线拟合的原理是y=kx+b,求斜率k和截距b。其中的x是数据产生的时间,从数据表的索引号转换求得,y是乘客量。还可以使用另一些方法进一步拟合曲线。如:
方法一曲线拟合:从图像数据可以看出,乘客数据走势更拟合一条微微上翘的曲线,设y是x的多项式函数,可使用多项式拟合:y=ax3+bx2+cx+d。
方法二多特征拟合:代入更多条件,比如利用年份、月份作为参数代入
一文搞懂梯度下降&反向传播
如果把神经网络模型比作一个黑箱,把模型参数比作黑箱上面一个个小旋钮,那么根据通用近似理论(universal approximation theorem),只要黑箱上的旋钮数量足够多,而且每个旋钮都被调节到合适的位置,那这个模型就可以实现近乎任意功能(可以逼近任意的数学模型)。
显然,这些旋钮(参数)不是由人工调节的,所谓的机器学习,就是通过程序来自动调节这些参数。神经网络不仅参数众多(少则十几万,多则上亿),而且网络是由线性层和非线性层交替叠加而成,上层参数的变化会对下层的输出产生非线性的影响,因此,早期的神经网络流派一度无法往多层方向发展,因为他们找不到能用于任意多层网络的、简洁的自动调节参数的方法。
直到上世纪80年代,祖师爷辛顿发明了反向传播算法,用输出误差的均方差(就是loss值)一层一层递进地反馈到各层神经网络,用梯度下降法来调节每层网络的参数。至此,神经网络才得以开始它的深度之旅。
本文用python自己动手实现梯度下降和反向传播算法。 请点击这里 到Github上查看源码。
梯度下降法是一种将输出误差反馈到神经网络并自动调节参数的方法,它通过计算输出误差的loss值( J )对参数 W 的导数,并沿着导数的反方向来调节 W ,经过多次这样的操作,就能将输出误差减小到最小值,即曲线的最低点。
虽然Tensorflow、Pytorch这些框架都实现了自动求导的功能,但为了彻底理解参数调节的过程,还是有必要自己动手实现梯度下降和反向传播算法。我相信你和我一样,已经忘了之前学的微积分知识,因此,到可汗学院复习下 Calculus
和 Multivariable Calculus 是个不错的方法,或是拜读 这篇关于神经网络矩阵微积分的文章 。
Figure2是求导的基本公式,其中最重要的是 Chain Rule ,它通过引入中间变量,将“ y 对 x 求导”的过程转换为“ y 对中间变量 u 求导,再乘以 u 对 x 求导”,这样就将一个复杂的函数链求导简化为多个简单函数求导。
如果你不想涉及这些求导的细节,可以跳过具体的计算,领会其思想就好。
对于神经网络模型: Linear - ReLu - Linear - MSE(Loss function) 来说,反向传播就是根据链式法则对 求导,用输出误差的均方差(MSE)对模型的输出求导,并将导数传回上一层神经网络,用于它们来对 w 、 b 和 x (上上层的输出)求导,再将 x 的导数传回到它的上一层神经网络,由此将输出误差的均方差通过递进的方式反馈到各神经网络层。
对于 求导的第一步是为这个函数链引入中间变量:
接着第二步是对各中间变量求导,最后才是将这些导数乘起来。
首先,反向传播的起点是对loss function求导,即 。 :
mse_grad()之所以用unsqueeze(-1)给导数增加一个维度,是为了让导数的shape和tensor shape保持一致。
linear层的反向传播是对 求导,它也是一个函数链,也要先对中间变量求导再将所有导数相乘:
这些中间变量的导数分别是:
对向量 求导,指的是对向量所有的标量求偏导( ),即: ,这个横向量也称为y的梯度。
这里 ,是一个向量,因此, 求导,指的是y的所有标量(y_1, y_2, ..., y_n)对向量x求偏导,即:
。
这个矩阵称为雅克比矩阵,它是个对角矩阵,因为 ,因此 。
同理, 。
因此,所有中间导数相乘的结果:
lin_grad() 中的inp.g、w.g和b.g分别是求 的导数,以inp.g为例,它等于 ,且需要乘以前面各层的导数,即 outp.g @ w.t() ,之所以要用点积运算符(@)而不是标量相乘,是为了让它的导数shape和tensor shape保持一致。同理,w.g和b.g也是根据相同逻辑来计算的。
ReLu层的求导相对来说就简单多了,当输入 = 0时,导数为0,当输入 0时,导数为1。
求导运算终于结束了,接下来就是验证我们的反向传播是否正确。验证方法是将forward_backward()计算的导数和Pytorch自动微分得到的导数相比较,如果它们相近,就认为我们的反向传播算法是正确的。
首先,将计算好的参数导数保存到w1g、b1g、w2g和b2g中,再用Pytorch的自动微分来求w11、b11、w22和b22的导数。
最后,用np.allclose()来比较导数间的差异,如果有任何一个导数不相近,assert就会报错。结果证明,我们自己动手实现的算法是正确的。
反向传播是遵循链式法则的,它将前向传播的输出作为输入,输入作为输出,通过递进的方式将求导这个动作从后向前传递回各层。神经网络参数的求导需要进行矩阵微积分计算,根据这些导数的反方向来调节参数,就可以让模型的输出误差的优化到最小值。
欢迎关注和点赞,你的鼓励将是我创作的动力
当前名称:mse函数python mse函数和sse
标题链接:http://azwzsj.com/article/dosghes.html