线性回归

主要内容包括:

  1. 线性回归的基本要素
  2. 线性回归模型从零开始的实现
  3. 线性回归模型使用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)=n1​i=1∑n​l(i)(w,b)=n1​i=1∑n​21​(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)我们在数据上迭代多次,通过在负梯度方向移动参数来更新每个参数。

矢量计算

在模型训练或预测时,我们常常会同时处理多个数据样本并用到矢量计算。在介绍线性回归的矢量计算表达式之前,让我们先考虑对两个向量相加的两种方法。

  1. 向量相加的一种方法是,将这两个向量按元素逐一做标量加法。
  2. 向量相加的另一种方法是,将这两个向量直接做矢量加法。
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])

两种实现方式的比较

  1. 从零开始的实现(推荐用来学习)

    能够更好的理解模型和神经网络底层的原理

  2. 使用pytorch的简洁实现

    能够更加快速地完成模型的设计与实现

L1线性回归的更多相关文章

  1. ML-线性模型 泛化优化 之 L1 L2 正则化

    认识 L1, L2 从效果上来看, 正则化通过, 对ML的算法的任意修改, 达到减少泛化错误, 但不减少训练误差的方式的统称 训练误差 这个就损失函数什么的, 很好理解. 泛化错误 假设 我们知道 预 ...

  2. L1-L11 jupter notebook 文件

    L1-L11 jupter notebook 文件下载地址 https://download.csdn.net/download/xiuyu1860/12157961 包括L12 Transforme ...

  3. 机器学习之路: python线性回归 过拟合 L1与L2正则化

    git:https://github.com/linyi0604/MachineLearning 正则化: 提高模型在未知数据上的泛化能力 避免参数过拟合正则化常用的方法: 在目标函数上增加对参数的惩 ...

  4. Spark2.0机器学习系列之12: 线性回归及L1、L2正则化区别与稀疏解

    概述 线性回归拟合一个因变量与一个自变量之间的线性关系y=f(x).       Spark中实现了:       (1)普通最小二乘法       (2)岭回归(L2正规化)       (3)La ...

  5. 《机器学习实战》学习笔记第八章 —— 线性回归、L1、L2范数正则项

    相关笔记: 吴恩达机器学习笔记(一) —— 线性回归 吴恩达机器学习笔记(三) —— Regularization正则化 ( 问题遗留: 小可只知道引入正则项能降低参数的取值,但为什么能保证 Σθ2  ...

  6. 【机器学习】--线性回归中L1正则和L2正则

    一.前述 L1正则,L2正则的出现原因是为了推广模型的泛化能力.相当于一个惩罚系数. 二.原理 L1正则:Lasso Regression L2正则:Ridge Regression 总结: 经验值 ...

  7. scikit-learn 线性回归算法库小结

    scikit-learn对于线性回归提供了比较多的类库,这些类库都可以用来做线性回归分析,本文就对这些类库的使用做一个总结,重点讲述这些线性回归算法库的不同和各自的使用场景. 线性回归的目的是要得到输 ...

  8. 线性回归 Linear Regression

    成本函数(cost function)也叫损失函数(loss function),用来定义模型与观测值的误差.模型预测的价格与训练集数据的差异称为残差(residuals)或训练误差(test err ...

  9. paper 126:[转载] 机器学习中的范数规则化之(一)L0、L1与L2范数

    机器学习中的范数规则化之(一)L0.L1与L2范数 zouxy09@qq.com http://blog.csdn.net/zouxy09 今天我们聊聊机器学习中出现的非常频繁的问题:过拟合与规则化. ...

随机推荐

  1. Inception V1-V4

    2019-05-29 20:56:02 一.Inception V1 当不知道在卷积神经网络中该使用1 * 1卷积还是3 * 3的卷积还是5 * 5的卷积或者是否需要进行pooling操作的时候,我们 ...

  2. 使用Jenkins与Docker持续集成与发布NetCore项目(实操篇)

    使用Jenkins与Docker持续集成与发布NetCore项目(教程一) 原文地址:https://www.cnblogs.com/Jackyye/p/12588182.html 基本环境 该教程的 ...

  3. Sprinboot 整合 RabbitMQ,RabbitMQ 消息重试机制

    当消费者消费消息的时候,出现错误,RabbitMQ 本身会有

  4. 高性能RabbitMQ

    1,什么是RabbitMq RabbitMQ是实现了高级消息队列协议(AMQP)的开源消息代理软件(亦称面向消息的中间件).RabbitMQ服务器是用Erlang语言编写的,而集群和故障转移是构建在开 ...

  5. iOS OCR

    身份证识别,又称 OCR 技术.OCR 技术是光学字符识别的缩写,是通过扫描等光学输入方式将各种票据.报刊.书籍.文稿及其它印刷品的文字转化为图像信息,再利用文字识别技术将图像信息转化为可以使用的计算 ...

  6. SpringBoot常见注解的解释

    @Component 这个注解类似SSM中的Controller和Service注解 ,将加了这个注解的类装配到Sping容器内,这样就可以在其他类用@Autowired注解实现依赖注入. @Conf ...

  7. Mob之社会化分享集成ShareSDK

    接着上篇顺便分享一篇自己使用 ShareSDK 的笔记,上篇我们集成了 SMSSDK 完成了短信接收验证码的功能,请参考Mob 之 短信验证集成 SMSSDK,如何在项目已经集成 SMSSDK 的情况 ...

  8. Redis对象——哈希(Hash)

    哈希在很多编程语言中都有着很广泛的应用,而在Redis中也是如此,在redis中,哈希类型是指Redis键值对中的值本身又是一个键值对结构,形如value=[{field1,value1},...{f ...

  9. D 【BJOI2018】求和

    时间限制 : 20000 MS   空间限制 : 565536 KB 评测说明 : 2s,512m 问题描述 master 对树上的求和非常感兴趣.他生成了一棵有根树,并且希望多次询问这棵树上一段路径 ...

  10. 基于 HTML5 WebGL 的楼宇智能化集成系统(二)

    前言       一套完整的可视化操作交互上,必不可少 2D/3D 的融合,在上期我们介绍了有关 3D 场景的环视漫游.巡视漫游以及动画效果,还包括了冷站场景.热站场景以及智慧末端的实现原理,本期主要 ...