第一章 线性回归
1.回归问题概述
设有变量
要求一组参数 使得平面 尽可能过所有点
2.误差项定义
每一个点的误差 都是相互独立的,并且服从相同的分布函数
这个分布函数是均值为 0 方差为 的高斯分布
3.独立同分布的意义
4.似然函数的作用
使用上标 表示这是第 i 个数据:
由于误差服从高斯分布
为了求解参数 将 (1) 代入 (2) 得:
当 与 接近时,这个概率会变大
因此将每一组数据的 相乘,得到的值的大小可以用于衡量所有数据的 与 之间接近的程度,这就是似然函数:
似然函数表示了什么样的参数与数据组合后为真实值
但是乘法比较难算,所以对这个连乘取对数化为加法:
可见 是一个常数项,所以只需要研究第二项,设第二项为一个新的函数
问题转化为求解参数 使得 最小
这个方法也叫做最小二乘法
这是一种推导思路
我还看到别的教程也有一种思路是,直接由总误差 = 得出要研究的方程就是
然后之后梯度下降也就这么直接来求导就好了
也是没问题的……就是说一开始从高斯分布开始的话还能知道 在统计上的意义,就是根据概率大小衡量数据与真实值的接近程度
5.正规方程
使用矩阵表达各个变量:
要使 最小,也就是求 的最小值点
对 求偏导:
令偏导数等于 0,得 即
对 能直接求极值点固然好,但是极值点公式 中 不一定存在,就是说正规方程不一定有解
因此要一步步试探到极值点,用梯度下降的方法,根本上是分治的思路
对矩阵求导的计算可以直接谷歌 The Matrix Cookbook
6.梯度下降
沿着梯度的反方向走
梯度下降不一定得到全局最优解
最简单的例子:
教程来源:www.bilibili.com/video/BV1Y7…
数据 x y 用一个 y = w * x 拟合,使用梯度下降找 w
代码:
import matplotlib.pyplot as plt
# prepare the training set
x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]
# initial guess of weight
w = 1.0
# define the model linear model y = w*x
def forward(x):
return x*w
#define the cost function MSE
def cost(xs, ys):
cost = 0
for x, y in zip(xs,ys):
y_pred = forward(x)
cost += (y_pred - y)**2
return cost / len(xs)
# define the gradient function gd
def gradient(xs,ys):
grad = 0
for x, y in zip(xs,ys):
grad += 2*x*(x*w - y)
return grad / len(xs)
epoch_list = []
cost_list = []
print('predict (before training)', 4, forward(4))
for epoch in range(100):
cost_val = cost(x_data, y_data)
grad_val = gradient(x_data, y_data)
w-= 0.01 * grad_val # 0.01 learning rate
print('epoch:', epoch, 'w=', w, 'loss=', cost_val)
epoch_list.append(epoch)
cost_list.append(cost_val)
print('predict (after training)', 4, forward(4))
plt.plot(epoch_list,cost_list)
plt.ylabel('cost')
plt.xlabel('epoch')
plt.show()
最简单,但是完整表示了梯度下降的过程,核心在:
cost_val = cost(x_data, y_data) # 使用更新后的 w 计算预测值
grad_val = gradient(x_data, y_data) # 计算梯度
w-= 0.01 * grad_val # 梯度下降
7.参数更新方法
新设一个损失函数:
1.批量梯度下降
容易得到最优解,但是由于每次考虑所有样本,速度很慢
2.随机梯度下降
每次找一个样本,迭代速度快,但不一定每次都朝着收敛的方向
但是批量梯度下降可以用并行算 n 个梯度然后加起来,而随机梯度下降,每计算一个梯度都要依赖上一个梯度,就不能并行了
3.小批量梯度下降
批量梯度下降和随机梯度下降的折衷方法
每次更新选择一小部分数据来算,实用
一组数据称为一个 batch
或者把所有数据称为一个 Batch 的话,一组数据就叫一个 Mini-Batch
注意:每一次梯度下降是同步更新所有参数
以小批量梯度下降为例,正确的同步更新:
以此类推……
错误的异步更新:
以此类推……
其实这个“错误的异步更新”就是“随机梯度下降”
这里就是说确认在 batch 里批量梯度下降,不要和随机梯度下降搞混了
8.优化参数设置
batch 的选择可以优化
学习率:步长 可以优化
如果随着迭代次数的增加,损失函数发生波动或者反而变大,那么可能需要减小学习率
9.特征缩放
多个变量的度量不同,数字之间相差的大小也不同,如果可以将所有的特征变量缩放到大致相同范围,这样会减少梯度算法的迭代。
特征缩放不一定非要落到 [-1,1] 之间,只要数据足够接近就可以
其中 为平均值 是标准差
特征缩放了之后,参数也会跟着变化
不缩放:
缩放:
化为:
所以
10.多项式回归
线性回归:
多项式回归:
对于多项式回归,仍然照着线性回归的思路
对误差函数求导算梯度就行了
这个时候特征缩放就很重要了。假设 的范围为 (0,100),那么 的范围为 (0,10000),相差就很大了
但是使用特征缩放的话,对 的缩放是不同的,最后能缩放到统一的范围
这个时候参数缩放的公式也推导也会跟线性回归时不同
11.梯度下降的问题
一是可能存在局部最优
一是可能存在鞍点,就是梯度 = 0 但不是极值
12.多层网络
图片来源:www.bilibili.com/video/BV1Y7…
简单的多层网络就是 y = w * x + b 的重复运用
W 权重 Weight
b 偏置 Bias
但是如果这么简单地堆叠,最终还是可以化简为一个线性回归式子
所以对每层输出的结果都加一个非线性函数,就可以将各层区分开
13.反向传播
在输入 x 和 w 的时候,可以计算出
在返回 z 的时候,可以算出
在计算梯度的时候,就可以根据链式法则算出
如果是线性模型,,那么就有
最后边一开始的梯度是怎么计算的呢,是因为把计算损失的过程也加入到了这个前向后向的一部分了
写模型的时候就是在构建这样一个计算图
例如 pytorch 的 tensor 类型包含两个成员,一个 data 一个 grad
14.pytorch 的使用
对 pytorch 的 tensor 变量进行原子运算的时候实际上是在构建计算图
调用 tensor 变量的 backward() 是计算一次梯度,计算的同时会清除这个变量的计算图,方便计算图在运行时变化
直接取标量使用 .item()
只进行数值计算而不构建计算图,可以对 tensor 变量的 .data 进行计算
最后参数 tensor 的梯度需要清零
有的时候可能需要梯度累加,所以并不能在 torch 里面写死自动清零
对之前的例子修改,使用 pytorch 重写得到:
import torch
x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]
w = torch.tensor([1.0]) # w的初值为1.0
w.requires_grad = True # 需要计算梯度
def forward(x):
return x*w # w是一个Tensor
def loss(x, y):
y_pred = forward(x)
return (y_pred - y)**2
print("predict (before training)", 4, forward(4).item())
for epoch in range(100):
for x, y in zip(x_data, y_data):
l =loss(x,y) # l是一个张量,tensor主要是在建立计算图 forward, compute the loss
l.backward() # backward,compute grad for Tensor whose requires_grad set to True
print('\tgrad:', x, y, w.grad.item())
w.data = w.data - 0.01 * w.grad.data # 权重更新时,注意grad也是一个tensor
w.grad.data.zero_() # after update, remember set the grad to zero
print('progress:', epoch, l.item()) # 取出loss使用l.item,不要直接使用l(l是tensor会构建计算图)
print("predict (after training)", 4, forward(4).item())
再进一步,使用 pytorch 的模块来定义
模型继承 torch.nn.Module
损失函数是一个标量,直接用 .backward()
optimizer 自动取到本模型及其子类的 .parameters(),只需要对 optimizer 调用 .step() 就可以更新一次参数
import torch
# prepare dataset
# x,y是矩阵,3行1列 也就是说总共有3个数据,每个数据只有1个特征
x_data = torch.tensor([[1.0], [2.0], [3.0]])
y_data = torch.tensor([[2.0], [4.0], [6.0]])
#design model using class
"""
our model class should be inherit from nn.Module, which is base class for all neural network modules.
member methods __init__() and forward() have to be implemented
class nn.linear contain two member Tensors: weight and bias
class nn.Linear has implemented the magic method __call__(),which enable the instance of the class can
be called just like a function.Normally the forward() will be called
"""
class LinearModel(torch.nn.Module):
def __init__(self):
super(LinearModel, self).__init__()
# (1,1)是指输入x和输出y的特征维度,这里数据集中的x和y的特征都是1维的
# 该线性层需要学习的参数是w和b 获取w/b的方式分别是~linear.weight/linear.bias
self.linear = torch.nn.Linear(1, 1)
def forward(self, x):
y_pred = self.linear(x)
return y_pred
model = LinearModel()
# construct loss and optimizer
# criterion = torch.nn.MSELoss(size_average = False)
criterion = torch.nn.MSELoss(reduction = 'sum')
optimizer = torch.optim.SGD(model.parameters(), lr = 0.01) # model.parameters()自动完成参数的初始化操作,这个地方我可能理解错了
# training cycle forward, backward, update
for epoch in range(100):
y_pred = model(x_data) # forward:predict
loss = criterion(y_pred, y_data) # forward: loss
print(epoch, loss.item())
optimizer.zero_grad() # the grad computer by .backward() will be accumulated. so before backward, remember set the grad to zero
loss.backward() # backward: autograd,自动计算梯度
optimizer.step() # update 参数,即更新w和b的值
print('w = ', model.linear.weight.item())
print('b = ', model.linear.bias.item())
x_test = torch.tensor([[4.0]])
y_test = model(x_test)
print('y_pred = ', y_test.data)
15.其他
15.1 PyCharm 使用
下载的话使用免费的 Community 版就好了
运行某 py 时可能会报错说没有导入某个包,这个时候去 File-Settings-Project:你的工程名-Python Interpreter,点击 + 号,就可以在弹出的界面选择你缺少的包并下载
有的时候下载完某个包之后 IDE 可能自动修改了你的 python 文件,给你多 import 一些包
比如我下载完 plotly 包之后,我的文件里多出一个
plotly.offline.init_notebook_mode()
然后他导致了一个 iplot can only run inside an IPython Notebook. 的错误,不知道是干嘛的,直接删了就好了
15.2 numpy 中 (n,1) 和 (n,) 的区别
stackoverflow.com/questions/2…
根据这个回答, 根本上的区别是存储方式的区别
个人感觉应该是 (n,1) 更好,因为别人的库都需要二维数组
15.3 pandas 中 df[column_name] 和 df[[column_name]] 的区别
假设 df 是一个 dataframe
df[column_name] 就是获取列名为 column_name 的这一列数据 大小为 (n,)
df[[column_name]] 也是获取列名为 column_name 的这一列数据 但大小为 (n,1)
df[] 外层的 [] 用于索引,造成这个区别的原因在于内层的东西
[] 内只传入一个 column_name 说明你只需要一列,所以 pandas 知道你只需要一列,所以返回 (n,)
但是如果传入一个列表,也就是传入一个 [a,b,c] 的话,pandas 就认为你需要多列,所以返回 (n,m) m 是你传入的列表中元素的个数
那么如果你传入一个 [column_name] 其实就是传入了一个列表,然后又因为列表中只有一个元素,所以 pandas 返回 (n,1)
15.4 pytorch 下载
在 pycharm 中下载的 pytorch 的包的名字叫做 torch,而不是 pytorch