机器学习基础备忘录
本文侧重代码实现,不讨论原理。
github图床出了一点问题,就不插图了。
文章目录
距离计算
欧
氏
距
离
:
d
(
x
,
y
)
=
∑
k
=
1
N
(
x
k
−
y
k
)
2
欧氏距离:d(x,y)=sqrt{sum_{k=1}^{N}(x_k-y_k)^2}
欧氏距离:d(x,y)=k=1∑N(xk−yk)2
曼
哈
顿
距
离
:
d
(
x
,
y
)
=
∑
k
=
1
N
∣
x
k
−
y
k
∣
曼哈顿距离:d(x,y)=sum_{k=1}^N|x_k-y_k|
曼哈顿距离:d(x,y)=k=1∑N∣xk−yk∣
切
比
雪
夫
距
离
:
d
(
x
,
y
)
=
max
(
∣
x
k
−
y
k
∣
)
切比雪夫距离:d(x,y)=max(|x_k-y_k|)
切比雪夫距离:d(x,y)=max(∣xk−yk∣)
余
弦
距
离
:
c
o
s
θ
=
x
1
x
2
+
y
1
y
2
x
1
2
+
x
2
2
y
1
2
+
y
2
2
余弦距离:costheta=frac{x_1x_2+y_1y_2}{sqrt{x_1^2+x_2^2}sqrt{y_1^2+y_2^2}}
余弦距离:cosθ=x12+x22
y12+y22
x1x2+y1y2
import numpy as np
dot1 = np.array([1,2]) # 第一个点的坐标(1,2)
dot2 = np.array([4,5]) # 第二个点的坐标(4,5)
d1 = np.sqrt(np.sum((dot1-dot2)**2)) # 欧氏距离
d2 = np.sum(np.abs(dot1-dot2)) # 曼哈顿距离
d3 = np.max(np.abs(dot1-dot2)) # 切比雪夫距离
d4 = np.dot(dot1,dot2) / np.sqrt(np.dot(dot1,dot1)*np.dot(dot2,dot2)) # 余弦距离
模型选择
留出法
将数据分为训练集和测试集,使用训练集生成模型,使用测试集检验模型准确率。
# 核心代码
train_test_split(data,data_lable,test_size=0.6)
# 完整代码
from sklearn.model_selection import train_test_split
import numpy as np
data = np.array([10,21,23,53,63]) # 需要划分的数据
data_lable = [0,1,2,3,4] # 上述数据的标签
"""
训练集数据, 测试集数据, 训练集标签, 测试集标签 =
train_test_split(数据列表,数据列表标签,test_size=训练集/总数)
"""
data_train, data_test, lable_train, lable_test = train_test_split(data,data_lable,test_size=0.6)
交叉验证法
相当于多次train_test_split
操作。将数据集划分为k个大小相似的子集,每次选取其中一个子集作为测试集,其他数据作为训练集。如[10,20,30,40],我们设k=4,则生成如下4种数据集:
训练集:[10,20,30],测试集:[40]
训练集:[10,20,40],测试集:[30]
训练集:[10,30,40],测试集:[20]
训练集:[20,30,40],测试集:[10]
代码如下:
# 核心代码
"""
将data数组分成3折 : KFold(n_splits=3).split(data)
train_index:训练集索引 使用data[train_index]获取训练集
test_index:测试集索引 使用data[test_index]获取测试集
"""
for train_index, test_index in KFold(n_splits=3).split(data):
...略...
# 完整代码
import numpy as np
from sklearn.model_selection import KFold
data = np.array([10,21,23,53,63,25]) # 需要划分的数据
for train_index, test_index in KFold(n_splits=3).split(data):
print("————————————————————————————————")
print("训练集索引:",train_index,"训练集:",data[train_index])
print("测试集索引:",test_index,"测试集:",data[test_index])
# 输出
————————————————————————————————
训练集索引: [2 3 4 5] 训练集: [23 53 63 25]
测试集索引: [0 1] 测试集: [10 21]
————————————————————————————————
训练集索引: [0 1 4 5] 训练集: [10 21 63 25]
测试集索引: [2 3] 测试集: [23 53]
————————————————————————————————
训练集索引: [0 1 2 3] 训练集: [10 21 23 53]
测试集索引: [4 5] 测试集: [63 25]
留一法
交叉验证的变种,每次只留一个数据作为测试集。例如有n个数需要被划分,则留一法就相当于k=n的交叉验证。
# 核心代码
for train_index, test_index in LeaveOneOut().split(data):
...略...
# 完整代码
from sklearn.model_selection import LeaveOneOut
import numpy as np
data = np.array([10,20,30,40])
for train_index, test_index in LeaveOneOut().split(data):
print("————————————————————————————————")
print("训练集索引:",train_index,"训练集:",data[train_index])
print("测试集索引:",test_index,"测试集:",data[test_index])
# 输出
————————————————————————————————
训练集索引: [1 2 3] 训练集: [20 30 40]
测试集索引: [0] 测试集: [10]
————————————————————————————————
训练集索引: [0 2 3] 训练集: [10 30 40]
测试集索引: [1] 测试集: [20]
————————————————————————————————
训练集索引: [0 1 3] 训练集: [10 20 40]
测试集索引: [2] 测试集: [30]
————————————————————————————————
训练集索引: [0 1 2] 训练集: [10 20 30]
测试集索引: [3] 测试集: [40]
性能度量
均方误差MSE
M
S
E
=
1
n
∑
i
=
1
n
(
f
(
x
i
)
−
y
i
)
2
MSE = frac{1}{n}sum_{i=1}^{n}(f(x_i)-y_i)^2
MSE=n1i=1∑n(f(xi)−yi)2
实现如下:
# 核心代码
"""
均方误差 = mean_squared_error(真值列表,预测值列表)
"""
result = mean_squared_error(y_true, y_pred)
import numpy as np
from sklearn.metrics import mean_squared_error
y_true = np.array([1, 2, 3, 4, 5, 6]) # 正确数据
y_pred = np.array([0, 2, 2, 4, 5, 7]) # 预测数据
result = mean_squared_error(y_true, y_pred)
# result结果为0.5
均方根误差RMSE
R
M
S
E
=
1
n
∑
i
=
1
n
(
f
(
x
i
)
−
y
i
)
2
RMSE = sqrt{frac{1}{n}sum_{i=1}^{n}(f(x_i)-y_i)^2}
RMSE=n1i=1∑n(f(xi)−yi)2
# 实现(MSE套一层根号即可)
result = np.sqrt( mean_squared_error(y_true, y_pred) )
平均绝对误差MAE
M
A
E
=
1
n
∑
i
=
1
m
∣
f
(
x
i
)
−
y
i
∣
MAE = frac{1}{n}sum_{i=1}^{m}|f(x_i)-y_i|
MAE=n1i=1∑m∣f(xi)−yi∣
实现起来非常简单,就是将MSE中的mean_squared_error
替换成mean_absolute_error
。
# 实现
import numpy as np
from sklearn.metrics import mean_absolute_erro
y_true = np.array([1, 2, 3, 4, 5, 6]) # 正确数据
y_pred = np.array([0, 2, 2, 4, 5, 7]) # 预测数据
result = mean_absolute_error(y_true, y_pred)
准确率
预测对的 / 所有
a
c
c
=
1
n
∑
i
=
1
n
(
f
(
x
i
)
=
y
i
)
acc=frac{1}{n}sum_{i=1}^{n}(f(x_i)=y_i)
acc=n1i=1∑n(f(xi)=yi)
# 核心代码
"""
准确率 = accuracy_score(正确数据列表,预测数据列表)
"""
result = accuracy_score(y_true,y_pred)
# 完整代码
import numpy as np
from sklearn.metrics import accuracy_score
y_true = np.array([1, 2, 3, 4, 5, 6]) # 正确
y_pred = np.array([0, 2, 2, 4, 5, 7]) # 预测
result = accuracy_score(y_true,y_pred)
混淆矩阵
真实情况预测结果 | 正例 | 反例 |
---|---|---|
正例 | TP(真正例) | FN(假反例) |
反例 | FP(假正例) | TN(真反例) |
-
查准率:预测为正中,预测正确的概率
P
=
T
P
T
P
+
F
P
P=frac{TP}{TP+FP}
P=TP+FPTP
-
查全率:真实情况为正中,预测正确的概率
R
=
T
P
T
P
+
F
N
R = frac{TP}{TP+FN}
R=TP+FNTP
-
准确率
A
C
C
=
T
P
+
T
N
T
P
+
F
P
+
T
N
+
F
N
ACC = frac{TP+TN}{TP+FP+TN+FN}
ACC=TP+FP+TN+FNTP+TN
通过生成真实数据与预测数据的混淆矩阵,可以更好的看出预测的情况。
# 核心代码
"""
混淆矩阵 = confusion_matrix(真实数据集,预测数据集,labels=标签集)
此处的标签集不好理解,看下面的样例就懂了
"""
result = confusion_matrix(y_true,y_pred,labels=[0,1])
# 完整代码
import numpy as np
from sklearn.metrics import confusion_matrix
y_pred = np.array([0, 1, 0, 1]) # 预测数据
y_true = np.array([1, 0, 1, 1]) # 正确数据
result = confusion_matrix(y_true,y_pred,labels=[0,1])
# 输出
[[0 1]
[2 1]]
上述输出可以用如下表格来解释
此处假设0为正例,1为反例
真实情况预测结果 | 0 | 1 |
---|---|---|
0 | 0 | 1 |
1 | 2 | 1 |
以上表格蕴含了以下信息:
真实情况 | 预测结果 | 预测次数 | 结果 |
---|---|---|---|
0 | 0 | 0 | 真正例TP = 0 |
0 | 1 | 1 | 假反例FN = 1,预测错误1次 |
1 | 0 | 2 | 假正例FP = 2,预测错误2次 |
1 | 1 | 1 | 真反例TN = 1,预测成功1次 |
查
准
率
=
T
P
T
P
+
F
P
=
0
0
+
2
=
0
查准率 = frac{TP}{TP+FP}=frac{0}{0+2}=0
查准率=TP+FPTP=0+20=0
查
全
率
=
T
P
T
P
+
F
N
=
0
0
+
1
=
0
查全率 = frac{TP}{TP+FN}=frac{0}{0+1}=0
查全率=TP+FNTP=0+10=0
准
确
率
=
T
P
+
T
N
T
P
+
F
P
+
T
N
+
F
N
=
0
+
1
0
+
2
+
1
+
1
=
1
4
准确率=frac{TP+TN}{TP+FP+TN+FN}=frac{0+1}{0+2+1+1} = frac{1}{4}
准确率=TP+FP+TN+FNTP+TN=0+2+1+10+1=41
实际上,sklearn也提供了直接计算查准率的函数precision_score
# 查准率
import numpy as np
from sklearn.metrics import precision_score
y_pred = np.array([0, 1, 0, 1]) # 预测数据
y_true = np.array([1, 0, 1, 1]) # 正确数据
accu = precision_score(y_true,y_pred,average='macro')
# accu结果为0.25
ROC曲线
真实情况预测结果 | 正例 | 反例 |
---|---|---|
正例 | TP(真正例) | FN(假反例) |
反例 | FP(假正例) | TN(真反例) |
R
O
C
曲
线
X
轴
:
f
p
r
=
F
P
F
P
+
T
N
=
真
实
情
况
中
:
反
例
预
测
错
误
的
真
实
情
况
中
:
反
例
总
和
ROC曲线X轴:fpr=frac{FP}{FP+TN}=frac{真实情况中:反例预测错误的}{真实情况中:反例总和}
ROC曲线X轴:fpr=FP+TNFP=真实情况中:反例总和真实情况中:反例预测错误的
R
O
C
曲
线
Y
轴
:
t
p
r
(
查
全
率
)
=
T
P
T
P
+
F
N
=
真
实
情
况
中
:
正
例
中
预
测
正
确
的
真
实
情
况
中
:
正
例
总
和
ROC曲线Y轴:tpr(查全率)=frac{TP}{TP+FN}=frac{真实情况中:正例中预测正确的}{真实情况中:正例总和}
ROC曲线Y轴:tpr(查全率)=TP+FNTP=真实情况中:正例总和真实情况中:正例中预测正确的
在ROC曲线中,AUC(曲线下的面积)值越大,说明该模型性能越好。
# 核心代码
"""
x轴列表, y轴列表, _ = roc_curve(真实数据,预测数据)
曲线下面积 = auc(x轴列表, y轴列表)
"""
fpr_x, tpr_y, _ = roc_curve(y_true, y_pred) # 生成ROC曲线的x、y值列表
auc = auc(fpr_x, tpr_y) # 计算曲线下的面积
# 完整代码
import numpy as np
from sklearn.metrics import roc_curve, auc
y_pred = np.array([0.1, 0.8, 0.2, 0.5, 0.5, 0.7, 0.3, 0.1]) # 预测数据
y_true = np.array([0, 1, 0, 1, 1, 1, 0, 1]) # 正确数据
fpr_x, tpr_y, _ = roc_curve(y_true, y_pred) # 生成ROC曲线的x、y值列表
auc = auc(fpr_x, tpr_y) # 计算曲线下的面积
# auc结果为0.8333333333333334
协方差Cov
C
o
v
(
X
,
Y
)
=
∑
i
=
1
n
(
x
i
−
x
‾
)
(
y
i
−
y
‾
)
n
−
1
Cov(X,Y)=frac{sum_{i=1}^{n}(x_i-overline{x})(y_i-overline{y})}{n-1}
Cov(X,Y)=n−1∑i=1n(xi−x)(yi−y)
通过一个实例来计算:
- 为方便计算,我们只定义两个点,每个点(样本)有两个特征:x与y
d
o
t
1
=
(
1
,
3
)
d
o
t
2
=
(
5
,
7
)
(
n
=
2
)
dot_1 = (1,3)\dot_2=(5,7)\(n=2)
dot1=(1,3)dot2=(5,7)(n=2)
-
用两个变量空间x,y分别表示特征对应的向量
x
=
[
1
5
]
,
y
=
[
3
7
]
x=begin{bmatrix} 1\5 end{bmatrix} , y=begin{bmatrix} 3\7 end{bmatrix}
x=[15],y=[37]
-
计算特征的均值
x
‾
=
3
,
y
‾
=
5
overline{x}=3,overline{y}=5
x=3,y=5
-
计算协方差
C
o
v
(
x
,
x
)
=
(
1
−
3
)
2
+
(
5
−
3
)
2
2
−
1
=
8
C
o
v
(
x
,
y
)
=
(
1
−
3
)
(
3
−
5
)
+
(
5
−
3
)
(
7
−
5
)
2
−
1
=
8
C
o
v
(
y
,
x
)
=
C
o
v
(
x
,
y
)
=
8
C
o
v
(
y
,
y
)
=
(
3
−
5
)
2
+
(
7
−
5
)
2
2
−
1
=
8
Cov(x,x) = frac{(1-3)^2+(5-3)^2}{2-1}=8\ Cov(x,y) = frac{(1-3)(3-5)+(5-3)(7-5)}{2-1}=8\ Cov(y,x)=Cov(x,y)=8\ Cov(y,y) = frac{(3-5)^2+(7-5)^2}{2-1}=8
Cov(x,x)=2−1(1−3)2+(5−3)2=8Cov(x,y)=2−1(1−3)(3−5)+(5−3)(7−5)=8Cov(y,x)=Cov(x,y)=8Cov(y,y)=2−1(3−5)2+(7−5)2=8
-
生成协方差矩阵
C
o
v
(
z
)
=
[
C
o
v
(
x
,
x
)
C
o
v
(
x
,
y
)
C
o
v
(
y
,
x
)
C
o
v
(
y
,
y
)
]
=
[
8
8
8
8
]
Cov(z)= begin{bmatrix} Cov(x,x) & Cov(x,y)\ Cov(y,x) & Cov(y,y) end{bmatrix} = begin{bmatrix} 8 & 8\ 8 & 8 end{bmatrix}
Cov(z)=[Cov(x,x)Cov(y,x)Cov(x,y)Cov(y,y)]=[8888]
下面用代码实现上述过程:
import numpy as np
x = np.array([1,5])
y = np.array([3,7])
z = np.stack([x,y]) # z=[[1,5],[3,7]]
result = np.cov(z) # 生成协方差矩阵
result的值
[[8. 8.]
[8. 8.]]
意义:协方差用来描述X和Y的相关程度
值范围 | 意义 |
---|---|
Cov( X , Y ) < 0 | X与Y负相关 |
Cov( X , Y ) > 0 | X与Y正相关 |
Cov( X , Y ) = 0 | X与Y不相关 |
Sklearn线性模型
线性回归
给一些点(xi,yi)
,用线性回归找出一条线y=wx+b
,该线能够最大程度的与点拟合。
通过这条回归线,我们能根据xi
预测出yi
的大概值。代码实现如下:
# 核心代码
model = LinearRegression() # 定义线性模型
model.fit(x, y) # 根据(x,y)点集生成线性模型
x_test = [[4], [5], [6]] # 测试数据
y_test = model.predict(x_test) # 模型预测结果
# 完整代码
import numpy as np
from sklearn.linear_model import LinearRegression
# 准备自变量与因变量
# 这部分不用看,只知道生成了(x,y)散点即可
x = np.array([0, 1, 2, 3, 4]) # 自变量x
y = 3 * x + 2 # 因变量y: [ 2 5 8 11 14]
x = x + np.random.rand(5) # 将点用随机数打乱
x = [[i] for i in x] # 行变列,如[1,2,3]会变成[[1],[2],[3]]
y = [[i] for i in y] # 行变列,这是sklearn包要求的格式
# 建立线性模型
model = LinearRegression() # 定义线性模型
model.fit(x, y) # 根据(x,y)点集生成线性模型
# 准备测试数据
x_test = [[4], [5], [6]]
# 打印预测结果
y_test = model.predict(x_test)
print(y_test)
print("w值:", model.coef_)
print("b截距值为:", model.intercept_)
# 输出
[[12.79914287]
[15.74000827]
[18.68087368]]
w值: [[2.9408654]]
b截距值为: [1.03568125]
上述代码生成了以下线性模型
y
=
w
x
+
b
=
2.9408654
x
+
1.03568125
y=wx+b=2.9408654x+1.03568125
y=wx+b=2.9408654x+1.03568125
将测试数据x代入该公式即可得到预测值y。
后面的pytorch部分将会通过梯度下降的方法来生成线性模型。
逻辑回归
以鸢尾花数据集为例。与上述线性回归极其相似,因此不作过多解释。
# 完整代码
from sklearn import datasets
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
# 获取数据集
iris = datasets.load_iris()
iris_x = iris.data
iris_y = iris.target
# 留出法划分数据集
x_train, x_test, y_train, y_test = train_test_split(iris_x, iris_y, test_size=0.1)
# 生成逻辑回归模型
model = LogisticRegression()
model.fit(x_train, y_train)
# 检验模型
y_pred = model.predict(x_test)
accu = accuracy_score(y_test, y_pred)
Pytorch简介
偏导数计算
计算
y
=
(
x
+
w
)
(
w
+
1
)
y=(x+w)(w+1)
y=(x+w)(w+1)对
x
x
x的偏导数,公式可以分解为下图:
#mermaid-svg-hVivz7Xc8LqeDbUO {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-hVivz7Xc8LqeDbUO .error-icon{fill:#552222;}#mermaid-svg-hVivz7Xc8LqeDbUO .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hVivz7Xc8LqeDbUO .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-hVivz7Xc8LqeDbUO .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hVivz7Xc8LqeDbUO .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hVivz7Xc8LqeDbUO .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hVivz7Xc8LqeDbUO .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hVivz7Xc8LqeDbUO .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hVivz7Xc8LqeDbUO .marker.cross{stroke:#333333;}#mermaid-svg-hVivz7Xc8LqeDbUO svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hVivz7Xc8LqeDbUO .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-hVivz7Xc8LqeDbUO .cluster-label text{fill:#333;}#mermaid-svg-hVivz7Xc8LqeDbUO .cluster-label span{color:#333;}#mermaid-svg-hVivz7Xc8LqeDbUO .label text,#mermaid-svg-hVivz7Xc8LqeDbUO span{fill:#333;color:#333;}#mermaid-svg-hVivz7Xc8LqeDbUO .node rect,#mermaid-svg-hVivz7Xc8LqeDbUO .node circle,#mermaid-svg-hVivz7Xc8LqeDbUO .node ellipse,#mermaid-svg-hVivz7Xc8LqeDbUO .node polygon,#mermaid-svg-hVivz7Xc8LqeDbUO .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hVivz7Xc8LqeDbUO .node .label{text-align:center;}#mermaid-svg-hVivz7Xc8LqeDbUO .node.clickable{cursor:pointer;}#mermaid-svg-hVivz7Xc8LqeDbUO .arrowheadPath{fill:#333333;}#mermaid-svg-hVivz7Xc8LqeDbUO .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hVivz7Xc8LqeDbUO .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hVivz7Xc8LqeDbUO .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-hVivz7Xc8LqeDbUO .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-hVivz7Xc8LqeDbUO .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hVivz7Xc8LqeDbUO .cluster text{fill:#333;}#mermaid-svg-hVivz7Xc8LqeDbUO .cluster span{color:#333;}#mermaid-svg-hVivz7Xc8LqeDbUO div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-hVivz7Xc8LqeDbUO :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
用pytorch构建上述公式,可以很容易的求出偏导数,代码如下:
import torch
# 生成公式 y=(x+w)*(w+1)
x = torch.tensor([2.0], requires_grad=True) # 求x的偏导数,必须使requires_grad=True,否则报错
w = torch.tensor([1.0])
a = torch.add(x, w) # a = x+w
b = torch.add(w, 1) # b = w+1
y = torch.mul(a, b) # y = a*b
# 反向传播,计算所有requires_grad=True张量(tensor)的导数
y.backward()
print(x.grad) # x的偏导数
# 结果
tensor([2.])
多次求导
backward(retain_graph=True)
可以保留计算图,再调用一次backward()
即可实现二阶求导。代码修改如下:
# 核心代码
y.backward(retain_graph=True) # 计算保留图,用于二次求导
print(x.grad) # x的一阶导
y.backward() # 二次求导
print(x.grad) # x的二阶导
# 完整代码
import torch
# 构建公式 y=(x+w)*(w+1)
x = torch.tensor([2.0], requires_grad=True) # 求x的偏导数,必须使requires_grad=True,否则报错
w = torch.tensor([1.0])
a = torch.add(x, w) # a = x+w
b = torch.add(w, 1) # b = w+1
y = torch.mul(a, b) # y = a*b
# 反向传播,计算所有requires_grad=True的导数
y.backward(retain_graph=True) # 计算保留图,用于二次求导
print(x.grad) # x的一阶导
y.backward()
print(x.grad) # x的二阶导
非标量输出
使用torch.cat()
函数我们可以将不同的函数结合到一起,实现下图计算:
#mermaid-svg-eCsndn90A4qvwq96 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-eCsndn90A4qvwq96 .error-icon{fill:#552222;}#mermaid-svg-eCsndn90A4qvwq96 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-eCsndn90A4qvwq96 .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-eCsndn90A4qvwq96 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-eCsndn90A4qvwq96 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-eCsndn90A4qvwq96 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-eCsndn90A4qvwq96 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-eCsndn90A4qvwq96 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-eCsndn90A4qvwq96 .marker.cross{stroke:#333333;}#mermaid-svg-eCsndn90A4qvwq96 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-eCsndn90A4qvwq96 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-eCsndn90A4qvwq96 .cluster-label text{fill:#333;}#mermaid-svg-eCsndn90A4qvwq96 .cluster-label span{color:#333;}#mermaid-svg-eCsndn90A4qvwq96 .label text,#mermaid-svg-eCsndn90A4qvwq96 span{fill:#333;color:#333;}#mermaid-svg-eCsndn90A4qvwq96 .node rect,#mermaid-svg-eCsndn90A4qvwq96 .node circle,#mermaid-svg-eCsndn90A4qvwq96 .node ellipse,#mermaid-svg-eCsndn90A4qvwq96 .node polygon,#mermaid-svg-eCsndn90A4qvwq96 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-eCsndn90A4qvwq96 .node .label{text-align:center;}#mermaid-svg-eCsndn90A4qvwq96 .node.clickable{cursor:pointer;}#mermaid-svg-eCsndn90A4qvwq96 .arrowheadPath{fill:#333333;}#mermaid-svg-eCsndn90A4qvwq96 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-eCsndn90A4qvwq96 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-eCsndn90A4qvwq96 .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-eCsndn90A4qvwq96 .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-eCsndn90A4qvwq96 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-eCsndn90A4qvwq96 .cluster text{fill:#333;}#mermaid-svg-eCsndn90A4qvwq96 .cluster span{color:#333;}#mermaid-svg-eCsndn90A4qvwq96 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-eCsndn90A4qvwq96 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;}
代码实现如下
# 核心代码
loss = torch.cat([y0, y1], dim=0) # dim=0代表横向拼接
loss_w = torch.tensor([1., 2.]) # 设置权重,y0为1,y1为2
loss.backward(gradient=loss_w) # 反向传播
# 完整代码
import torch
# 构建两个公式
w = torch.tensor([1.], requires_grad=True)
x = torch.tensor([2.], )
a = torch.add(w, x)
b = torch.add(w, 3)
y0 = torch.mul(a, b) # y0 = (x+w)(w+3)
y1 = torch.add(a, b) # y1 = (x+w)+(w+3)
# 合并公式
loss = torch.cat([y0, y1], dim=0) # dim=0代表横向拼接
loss_w = torch.tensor([1., 2.]) # 设置权重,y0为1,y1为2
loss.backward(gradient=loss_w) # 反向传播
print(w.grad) # w的偏导数
# 输出
tensor([11.])
线性回归
问题:给定若干
(
x
,
y
)
(x,y)
(x,y)点集,找出一条直线
y
=
w
x
+
b
y=wx+b
y=wx+b,使所有点到直线的距离之和最小。
目标:找到合适的
w
w
w与
b
b
b,使损失函数
L
=
1
N
∑
i
=
1
N
(
w
x
i
+
b
−
y
t
r
u
e
)
2
L=frac{1}{N}sum_{i=1}^{N}(wx_i+b-y_{true})^2
L=N1∑i=1N(wxi+b−ytrue)2 的值最小。
方法:我们需要对损失函数关于
w
w
w和
b
b
b求导:
∂
L
∂
w
frac{partial{L}}{partial{w}}
∂w∂L,
∂
L
∂
b
frac{partial{L}}{partial{b}}
∂b∂L,然后使用公式
w
t
+
1
=
w
t
−
μ
∂
L
∂
w
b
t
+
1
=
b
t
−
μ
∂
L
∂
b
(
μ
:
学
习
率
,
梯
度
下
降
的
跨
度
)
w_{t+1}=w_t-mufrac{partial{L}}{partial{w}} \ b_{t+1}=b_t-mufrac{partial{L}}{partial{b}} \(mu:学习率,梯度下降的跨度)
wt+1=wt−μ∂w∂Lbt+1=bt−μ∂b∂L(μ:学习率,梯度下降的跨度)
不断调整
w
w
w与
b
b
b的值,直到得到合适的线性模型。
import matplotlib.pyplot as plt
import torch
# 准备自变量与因变量
# 这部分不用看,只知道生成了(x,y)散点即可
x = torch.rand(20, 1) * 10 # [20*1]的0-1的随机数 * 10
y = 2 * x + (5 + torch.randn(20, 1)) # y = 2x + 5
# 学习率
mu = 0.05
# 构建线性回归参数 y_pred = wx + b
w = torch.tensor(5.0, requires_grad=True) # 初始化w
b = torch.tensor(10.0, requires_grad=True) # 初始化b
# 迭代训练1000次
for i in range(1000):
"""向前传播,计算预测值 y_pred = wx + b"""
wx = torch.mul(w, x) # wx = w * x
y_pred = torch.add(wx, b) # y_pred = w * x + b
# 计算MSE loss
# 目的是为了使loss尽可能小
loss = (0.5 * (y - y_pred) ** 2).mean()
# 反向传播,求b与w的偏导数
loss.backward()
# 更新参数
# 此处等同于 w = w.sub(w,lr*w.grad)
# 后面带下划线的都是in-place的,会将调用者改变
w.data.sub_(mu * w.grad) # 调整w的值
b.data.sub_(mu * b.grad) # 调整b的值
# 更新参数后一定要清零梯度
if i != 999:
w.grad.zero_()
b.grad.zero_()
# 每隔50次循环输出一次图像
if i % 50 == 0:
plt.scatter(x.data.numpy(), y.data.numpy()) # 散点
plt.plot(x.data.numpy(), y_pred.data.numpy()) # 回归直线
plt.show()