L1线性回归
线性回归
主要内容包括:
- 线性回归的基本要素
- 线性回归模型从零开始的实现
- 线性回归模型使用pytorch的简洁实现
代码下载地址
https://download.csdn.net/download/xiuyu1860/12156391
pytorch版本为1.3
线性回归的基本要素
模型
构建一个假设模型,假设价格只取决于房屋状况的两个因素,即面积(平方米)和房龄(年)。
price=warea⋅area+wage⋅age+b
\mathrm{price} = w_{\mathrm{area}} \cdot \mathrm{area} + w_{\mathrm{age}} \cdot \mathrm{age} + b
price=warea⋅area+wage⋅age+b
接下来我们希望通过构建机器学习模型学习探索价格与这两个因素的具体关系,即通过学习自动确定变量warea和wagew_{\mathrm{area}} 和w_{\mathrm{age}}warea和wage的值 。
数据集
我们通常收集一系列的真实数据,例如多栋房屋的真实售出价格和它们对应的面积和房龄。我们希望在这个数据上面寻找模型参数来使模型的预测价格与真实价格的误差最小。在机器学习术语里,该数据集被称为训练数据集(training data set)或训练集(training set),一栋房屋被称为一个样本(sample),其真实售出价格叫作标签(label),用来预测标签的两个因素叫作特征(feature)。特征用来表征样本的特点。
损失函数
在模型训练中,我们需要衡量价格预测值与真实值之间的误差。通常我们会选取一个非负数作为误差,且数值越小表示误差越小。一个常用的选择是平方函数。 它在评估索引为 iii 的样本误差的表达式为
l(i)(w,b)=12(y^(i)−y(i))2,
l^{(i)}(\mathbf{w}, b) = \frac{1}{2} \left(\hat{y}^{(i)} - y^{(i)}\right)^2,
l(i)(w,b)=21(y^(i)−y(i))2,
L(w,b)=1n∑i=1nl(i)(w,b)=1n∑i=1n12(w⊤x(i)+b−y(i))2.
L(\mathbf{w}, b) =\frac{1}{n}\sum_{i=1}^n l^{(i)}(\mathbf{w}, b) =\frac{1}{n} \sum_{i=1}^n \frac{1}{2}\left(\mathbf{w}^\top \mathbf{x}^{(i)} + b - y^{(i)}\right)^2.
L(w,b)=n1i=1∑nl(i)(w,b)=n1i=1∑n21(w⊤x(i)+b−y(i))2.
优化函数 - 随机梯度下降
当模型和损失函数形式较为简单时,上面的误差最小化问题的解可以直接用公式表达出来。这类解叫作解析解(analytical solution)。本节使用的线性回归和平方误差刚好属于这个范畴。然而,大多数深度学习模型并没有解析解,只能通过优化算法有限次迭代模型参数来尽可能降低损失函数的值。这类解叫作数值解(numerical solution)。
在求数值解的优化算法中,小批量随机梯度下降(mini-batch stochastic gradient descent)在深度学习中被广泛使用。它的算法很简单:先选取一组模型参数的初始值,如随机选取;接下来对参数进行多次迭代,使每次迭代都可能降低损失函数的值。在每次迭代中,先随机均匀采样一个由固定数目训练数据样本所组成的小批量(mini-batch)B\mathcal{B}B,然后求小批量中数据样本的平均损失有关模型参数的导数(梯度),最后用此结果与预先设定的一个正数的乘积作为模型参数在本次迭代的减小量。
(w,b)←(w,b)−η∣B∣∑i∈B∂(w,b)l(i)(w,b)
(\mathbf{w},b) \leftarrow (\mathbf{w},b) - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}} \partial_{(\mathbf{w},b)} l^{(i)}(\mathbf{w},b)
(w,b)←(w,b)−∣B∣ηi∈B∑∂(w,b)l(i)(w,b)
学习率: η\etaη代表在每次优化中,能够学习的步长的大小
批量大小: B\mathcal{B}B是小批量计算中的批量大小batch size
总结一下,优化函数的有以下两个步骤:
- (i)初始化模型参数,一般来说使用随机初始化;
- (ii)我们在数据上迭代多次,通过在负梯度方向移动参数来更新每个参数。
矢量计算
在模型训练或预测时,我们常常会同时处理多个数据样本并用到矢量计算。在介绍线性回归的矢量计算表达式之前,让我们先考虑对两个向量相加的两种方法。
- 向量相加的一种方法是,将这两个向量按元素逐一做标量加法。
- 向量相加的另一种方法是,将这两个向量直接做矢量加法。
import torch
import time
# init variable a, b as 1000 dimension vector
n = 1000
a = torch.ones(n)
b = torch.ones(n)
# define a timer class to record time
class Timer(object):
"""Record multiple running times."""
def __init__(self):
self.times = []
self.start()
def start(self):
# start the timer
self.start_time = time.time()
def stop(self):
# stop the timer and record time into a list
self.times.append(time.time() - self.start_time)
return self.times[-1]
def avg(self):
# calculate the average and return
return sum(self.times)/len(self.times)
def sum(self):
# return the sum of recorded time
return sum(self.times)
现在我们可以来测试了。首先将两个向量使用for循环按元素逐一做标量加法。
timer = Timer()
c = torch.zeros(n)
for i in range(n):
c[i] = a[i] + b[i]
'%.5f sec' % timer.stop()
'0.01432 sec'
另外是使用torch来将两个向量直接做矢量加法:
timer.start()
d = a + b
'%.5f sec' % timer.stop()
'0.00024 sec'
结果很明显,后者比前者运算速度更快。因此,我们应该尽可能采用矢量计算,以提升计算效率。
一、线性回归模型从零开始的实现
1.1 导入必的包
# import packages and modules
%matplotlib inline
import torch
from IPython import display
from matplotlib import pyplot as plt
import numpy as np
import random
print(torch.__version__)
1.3.0
1.2 模拟生成实验用数据集
使用线性模型来生成数据集,生成一个1000个样本的数据集,下面是用来生成数据的线性关系:
price=warea⋅area+wage⋅age+b
\mathrm{price} = w_{\mathrm{area}} \cdot \mathrm{area} + w_{\mathrm{age}} \cdot \mathrm{age} + b
price=warea⋅area+wage⋅age+b
# set input feature number
num_inputs = 2
# set example number
num_examples = 1000
# set true weight and bias in order to generate corresponded label
true_w = [2, -3.4]
true_b = 4.2
features = torch.randn(num_examples, num_inputs,
dtype=torch.float32)
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()),
dtype=torch.float32)
1.3 模拟数据的可视化
plt.scatter(features[:, 1].numpy(), labels.numpy(), 1);
1.4读取模拟生成的数据集
在训练模型的时候,我们需要遍历数据集并不断读取小批量数据样本。
# 定义一个函数data_iter:它每次返回 batch_size(批量大小)个随机样本的特征和标签。
def data_iter(batch_size, features, labels):
num_examples = len(features)
indices = list(range(num_examples))
# 样本的读取顺序是随机的
random.shuffle(indices) # random read 10 samples
for i in range(0, num_examples, batch_size):
j = torch.LongTensor(indices[i: min(i + batch_size, num_examples)]) # the last time may be not enough for a whole batch
#函数根据索引返回对应元素
yield features.index_select(0, j), labels.index_select(0, j)
# 验证torch.index_select() 功能的程序:
j=torch.LongTensor([1,2,3,4,5,6,7,8,10])
features.index_select(0,j)
#torch.index_select()
#在指定维度dim上选择,比如选取某些行、某些列。返回不Tensor不与原Tensor共享内存。
#在python中,dim=0,表示按行取,dim=1表示按列取
tensor([[-0.8797, 1.0840],
[ 1.4686, 0.5604],
[ 0.6072, -1.0188],
[-0.3210, 1.1137],
[ 0.4691, 1.2000],
[-0.8294, -0.8613],
[ 0.9604, -0.2414],
[ 0.3751, -0.8777],
[-0.2483, 0.1386]])
#读取第一个小批量数据样本并打印。每个批量的特征形状为(10, 2),
#分别对应批量大小和输入个数;标签形状为批量大小。
# 注意去掉break 之后 将输出所有批次数据
batch_size = 10
for X, y in data_iter(batch_size, features, labels):
print(X, '\n', y)
break
tensor([[ 0.6798, 0.2157],
[-0.3323, -0.7287],
[ 0.7562, -0.0557],
[-0.2248, -0.6173],
[-3.0879, -0.7436],
[-0.9020, -0.1528],
[ 0.4947, -0.2986],
[ 1.4328, -0.7418],
[ 0.3510, -0.3221],
[ 0.5044, -1.0165]])
tensor([4.8287, 5.9974, 5.8995, 5.8659, 0.5635, 2.9160, 6.1984, 9.6012, 6.0001,
8.6722])
1.5初始化模型参数
我们将权重初始化成均值为0、标准差为0.01的正态随机数,偏差则初始化成0:
w = torch.tensor(np.random.normal(0, 0.01, (num_inputs, 1)), dtype=torch.float32)# 注意本问题num_inputs 为2
b = torch.zeros(1, dtype=torch.float32)
#模型训练中需要对这些参数求梯度来迭代参数的值,因此我们需要创建它们的梯度:
w.requires_grad_(requires_grad=True)
b.requires_grad_(requires_grad=True)
tensor([0.], requires_grad=True)
1.6定义模型
定义用来训练参数的训练模型:
price=warea⋅area+wage⋅age+b
\mathrm{price} = w_{\mathrm{area}} \cdot \mathrm{area} + w_{\mathrm{age}} \cdot \mathrm{age} + b
price=warea⋅area+wage⋅age+b
#线性回归的矢量计算表达式的实现,我们使用 torch.mm做矩阵乘法:
#torch.mm(a, b)是矩阵a和b矩阵相乘,torch.mul(a, b)是矩阵a和b对应位相乘
def linreg(X, w, b):
return torch.mm(X, w) + b
1.7 定义损失函数
我们使用的是均方误差损失函数:
l(i)(w,b)=12(y^(i)−y(i))2,
l^{(i)}(\mathbf{w}, b) = \frac{1}{2} \left(\hat{y}^{(i)} - y^{(i)}\right)^2,
l(i)(w,b)=21(y^(i)−y(i))2,
为实施运算,用y.view(y_hat.size())把 y 变形成 y_hat 的形状,防止y_hat,和y维度不一样
torch 类型可使用view()来改变Tensor形状。如:y = x.view(12),z = x.view(-1,6),其中-1表示所指的维度可以根据其他维度的值推算出来。
1.view()返回的新tensor与源tensor共享内存,就是一个tensor,改变其中的一个,另一个也会跟着改变,view仅仅是改变了对这个张量的观察角度。
2.此外reshape()函数可以改变形状,但是此函数并不能保证返回的是其拷贝,不推荐使用。
3.推荐使用clone创造一个副本,然后使用view。使用clone还有一个好处是会被记录在计算图中,即梯度回传到副本时也会传到源Tensor。
def squared_loss(y_hat, y):
return (y_hat - y.view(y_hat.size())) ** 2 / 2
1.8定义优化函数
在这里优化函数使用的是小批量随机梯度下降:
(w,b)←(w,b)−η∣B∣∑i∈B∂(w,b)l(i)(w,b)
(\mathbf{w},b) \leftarrow (\mathbf{w},b) - \frac{\eta}{|\mathcal{B}|} \sum_{i \in \mathcal{B}} \partial_{(\mathbf{w},b)} l^{(i)}(\mathbf{w},b)
(w,b)←(w,b)−∣B∣ηi∈B∑∂(w,b)l(i)(w,b)
以下的sgd函数实现了小批量随机梯度下降算法。它通过不断迭代模型参数来优化损失函数。
这里自动求梯度模块计算得来的梯度是一个批量样本的梯度和。我们将它除以批量大小来得到平均值。
def sgd(params, lr, batch_size):
for param in params:
param.data -= lr * param.grad / batch_size # ues .data to operate param without gradient track
训练
当数据集、模型、损失函数和优化函数定义完了之后就可来准备进行模型的训练了。
# super parameters init
lr = 0.03
num_epochs = 5
net = linreg
loss = squared_loss
# training
for epoch in range(num_epochs): # training repeats num_epochs times
# in each epoch, all the samples in dataset will be used once
# X is the feature and y is the label of a batch sample
for X, y in data_iter(batch_size, features, labels):
l = loss(net(X, w, b), y).sum()
# calculate the gradient of batch sample loss
l.backward()
# using small batch random gradient descent to iter model parameters
sgd([w, b], lr, batch_size)
# reset parameter gradient
#在grad更新时,每一次运算后都需要将上一次的梯度记录清空
w.grad.data.zero_()
b.grad.data.zero_()
train_l = loss(net(features, w, b), labels)
print('epoch %d, loss %f' % (epoch + 1, train_l.mean().item()))
epoch 1, loss 0.035207
epoch 2, loss 0.000123
epoch 3, loss 0.000054
epoch 4, loss 0.000054
epoch 5, loss 0.000054
w, true_w, b, true_b
(tensor([[ 1.9999],
[-3.4002]], requires_grad=True),
[2, -3.4],
tensor([4.2003], requires_grad=True),
4.2)
二、线性回归模型使用pytorch的简洁实现
import torch
from torch import nn
import numpy as np
torch.manual_seed(1)
print(torch.__version__)
torch.set_default_tensor_type('torch.FloatTensor')
1.3.0
2.1生成数据集
在这里生成数据集跟从零开始的实现中是完全一样的。
num_inputs = 2
num_examples = 1000
true_w = [2, -3.4]
true_b = 4.2
features = torch.tensor(np.random.normal(0, 1, (num_examples, num_inputs)), dtype=torch.float)
labels = true_w[0] * features[:, 0] + true_w[1] * features[:, 1] + true_b
labels += torch.tensor(np.random.normal(0, 0.01, size=labels.size()), dtype=torch.float)
2.2读取数据集
import torch.utils.data as Data
batch_size = 10
# combine featues and labels of dataset
dataset = Data.TensorDataset(features, labels)
# put dataset into DataLoader
data_iter = Data.DataLoader(
dataset=dataset, # torch TensorDataset format
batch_size=batch_size, # mini batch size
shuffle=True, # whether shuffle the data or not
num_workers=2, # read data in multithreading
)
for X, y in data_iter:
print(X, '\n', y)
break
tensor([[ 0.0949, -2.0367],
[ 0.0957, -2.4354],
[ 0.1520, -1.5686],
[ 1.3453, 0.1253],
[ 0.3076, -1.0100],
[-0.6013, 1.6175],
[ 0.2898, 0.2359],
[ 0.4352, -0.4930],
[ 0.9694, -0.8326],
[-1.0968, -0.2515]])
tensor([11.3024, 12.6900, 9.8462, 6.4771, 8.2533, -2.4928, 3.9811, 6.7626,
8.9806, 2.8489])
2.3定义模型
nn.Module是pytorch中提供的一个类,是所有神经网络模块的基类.我们自定义的模块要继承这个基类.
class LinearNet(nn.Module): # 继承 torch 的 Module ,前面已经有from torch import nn
def __init__(self, n_feature):
super(LinearNet, self).__init__() # call father function to init, 继承 Module 的 __init__ 功能,
# 定义每层用什么样的形式
self.linear = nn.Linear(n_feature, 1) ## 注意 这里的linear是nn.Linear类型,可理解为线性层,支持“喂”数据作为输入,即linear(x)
# function prototype: `torch.nn.Linear(in_features, out_features, bias=True)`
def forward(self, x):#这同时也是 Module 中的 forward 功能
y = self.linear(x)
return y
net = LinearNet(num_inputs)
print(net)
LinearNet(
(linear): Linear(in_features=2, out_features=1, bias=True)
)
super( LinearNet, self).init()
对继承自父类的属性进行初始化
首先找到LinearNet的父类(比如是类A),然后把类LinearNet的对象self转换为类A的对象,
然后“被转换”的类A对象调用自己的__init__函数
## 理解nn.linear 的例子
import torch
nn1 = torch.nn.Linear(100, 50)
input1 = torch.randn(140, 100)
output1 = nn1(input1)
output1.size()
torch.Size([140, 50])
# ways to init a multilayer network
# method one
net = nn.Sequential(
nn.Linear(num_inputs, 1)
# other layers can be added here
)
# method two
net = nn.Sequential()
net.add_module('linear', nn.Linear(num_inputs, 1))
# net.add_module ......
# method three
from collections import OrderedDict
net = nn.Sequential(OrderedDict([
('linear', nn.Linear(num_inputs, 1))
# ......
]))
print(net)
print(net[0])
Sequential(
(linear): Linear(in_features=2, out_features=1, bias=True)
)
Linear(in_features=2, out_features=1, bias=True)
初始化模型参数
from torch.nn import init
init.normal_(net[0].weight, mean=0.0, std=0.01)
init.constant_(net[0].bias, val=0.0) # or you can use `net[0].bias.data.fill_(0)` to modify it directly
Parameter containing:
tensor([0.], requires_grad=True)
for param in net.parameters():
print(param)
Parameter containing:
tensor([[-0.0152, 0.0038]], requires_grad=True)
Parameter containing:
tensor([0.], requires_grad=True)
定义损失函数
loss = nn.MSELoss() # nn built-in squared loss function
# function prototype: `torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')`
定义优化函数
import torch.optim as optim
optimizer = optim.SGD(net.parameters(), lr=0.03) # built-in random gradient descent function
print(optimizer) # function prototype: `torch.optim.SGD(params, lr=, momentum=0, dampening=0, weight_decay=0, nesterov=False)`
SGD (
Parameter Group 0
dampening: 0
lr: 0.03
momentum: 0
nesterov: False
weight_decay: 0
)
训练
num_epochs = 3
for epoch in range(1, num_epochs + 1):
for X, y in data_iter:
output = net(X)
l = loss(output, y.view(-1, 1))
optimizer.zero_grad() # reset gradient, equal to net.zero_grad()
l.backward()
optimizer.step()
print('epoch %d, loss: %f' % (epoch, l.item()))
epoch 1, loss: 0.000290
epoch 2, loss: 0.000128
epoch 3, loss: 0.000107
# result comparision
dense = net[0]
print(true_w, dense.weight.data)
print(true_b, dense.bias.data)
[2, -3.4] tensor([[ 1.9997, -3.3999]])
4.2 tensor([4.2002])
两种实现方式的比较
从零开始的实现(推荐用来学习)
能够更好的理解模型和神经网络底层的原理
使用pytorch的简洁实现
能够更加快速地完成模型的设计与实现
L1线性回归的更多相关文章
- ML-线性模型 泛化优化 之 L1 L2 正则化
认识 L1, L2 从效果上来看, 正则化通过, 对ML的算法的任意修改, 达到减少泛化错误, 但不减少训练误差的方式的统称 训练误差 这个就损失函数什么的, 很好理解. 泛化错误 假设 我们知道 预 ...
- L1-L11 jupter notebook 文件
L1-L11 jupter notebook 文件下载地址 https://download.csdn.net/download/xiuyu1860/12157961 包括L12 Transforme ...
- 机器学习之路: python线性回归 过拟合 L1与L2正则化
git:https://github.com/linyi0604/MachineLearning 正则化: 提高模型在未知数据上的泛化能力 避免参数过拟合正则化常用的方法: 在目标函数上增加对参数的惩 ...
- Spark2.0机器学习系列之12: 线性回归及L1、L2正则化区别与稀疏解
概述 线性回归拟合一个因变量与一个自变量之间的线性关系y=f(x). Spark中实现了: (1)普通最小二乘法 (2)岭回归(L2正规化) (3)La ...
- 《机器学习实战》学习笔记第八章 —— 线性回归、L1、L2范数正则项
相关笔记: 吴恩达机器学习笔记(一) —— 线性回归 吴恩达机器学习笔记(三) —— Regularization正则化 ( 问题遗留: 小可只知道引入正则项能降低参数的取值,但为什么能保证 Σθ2 ...
- 【机器学习】--线性回归中L1正则和L2正则
一.前述 L1正则,L2正则的出现原因是为了推广模型的泛化能力.相当于一个惩罚系数. 二.原理 L1正则:Lasso Regression L2正则:Ridge Regression 总结: 经验值 ...
- scikit-learn 线性回归算法库小结
scikit-learn对于线性回归提供了比较多的类库,这些类库都可以用来做线性回归分析,本文就对这些类库的使用做一个总结,重点讲述这些线性回归算法库的不同和各自的使用场景. 线性回归的目的是要得到输 ...
- 线性回归 Linear Regression
成本函数(cost function)也叫损失函数(loss function),用来定义模型与观测值的误差.模型预测的价格与训练集数据的差异称为残差(residuals)或训练误差(test err ...
- paper 126:[转载] 机器学习中的范数规则化之(一)L0、L1与L2范数
机器学习中的范数规则化之(一)L0.L1与L2范数 zouxy09@qq.com http://blog.csdn.net/zouxy09 今天我们聊聊机器学习中出现的非常频繁的问题:过拟合与规则化. ...
随机推荐
- P1969 积木大赛 题解
原题链接 简要题意: 每次把一段区间 \(+1\),问得到 \(a\) 数组的最小次数. 我们可以把 \(+1\) 得到 \(a\) 换成,从 \(a\) 依次 \(-1\) 得到 \(0\). 算法 ...
- Android LinearLayout线性布局详解
为了更好地管理Android应用的用户界面里的各组件,Android提供了布局管理器.通过使用布局管理器,Android应用图形用户界面具有良好的平台无关性.推荐使用布局管理器来管理组件的分布.大小, ...
- 2020 | 可替代Selenium的测试框架Top15
本文首发于 微信公众号: 软测小生 Selenium是一种开源自动测试工具.它可以跨不同的浏览器和平台在Web应用程序上执行功能,回归,负载测试.Slenium是最好的工具之一,但确实有一些缺点. 业 ...
- Git使用的一些问题:.gitignore规则不生效、git同步代码至github和gitee
Git忽略规则及.gitignore规则不生效的解决办法 .gitignore 的基本使用 在git中如果想忽略掉某个文件,不让这个文件提交到版本库中,可以使用修改根目录中 .gitignore 文件 ...
- 【Pytest05】全网最全最新的Pytest框架之用例分组执行
一.Fixture用例分组运行常用于冒烟测试,分模块运行等 pytest.ini配置文件中增加分组参数markers来实现用例分组,如: markers = g1:组一 smoke:冒烟测试 pyte ...
- coding++:事务管理 隔离级别
在声明事务时,只需要通过value属性指定配置的事务管理器名即可,例如:@Transactional(value="transactionManagerPrimary"). 除了指 ...
- Cygwin工具编译Ardupilot方法
注意:该编译方法生成的固件基于Chibios系统,如果想要Nuttx系统固件,需采用make编译,步骤见make编译说明部分. 软件安装准备 安装Cygwin 打开链接www.cygwin.com/i ...
- SSH免密配置
1. 在三个节点生成秘钥cd ~/.sshssh-keygen -t rsa 2.合并三个节点的密码cat id_rsa.pub >> authorized_keysssh postgre ...
- [vijos1304]回文数<模拟>
题目链接:https://vijos.org/p/1304 好久没写博客了,最近一直打不出题,感觉自己是废了,今天做了一道模拟水题,但还是半天没过,后来才发现是忘记考虐10以上的进制是带有字母的,然后 ...
- Mysql 截取字符串总结
MySQL 字符串截取函数:left(), right(), substring(), substring_index().还有 mid(), substr().其中,mid(), substr() ...