全连接层(线性连接层)-torch.nn.Linear-学习
一、前言
虽然框架用起来爽是爽,但是探究一下其底层实现会更爽哈哈。有时知道怎么用,但是不知其所以然很恼火的。当然这并不是说我们要钻牛角尖,做理解底层实现这样的操作,前提不要耽搁我们的正常工作。
现在的社会真是快节奏,人变得浮躁,不愿再静下心去长时间学习,或自己愿意但身体不受控制学着学着搞别的去了。
学习最注重效率,效率的提升常伴随着方法的改进,时间的利用,资源的把握等方面。因此我们希望提高效率,尽快把手头工作做完,然后进一步学习自己求知的,深入的知识。不断充实自己,不断学习是我们的终极目标。
二、剖析
(一)、线性计算表达式
x先看一下官方对于该层的计算表达式定义:
y
=
x
A
T
+
b
y=xA^T+b
y=xAT+b
- x x x的输入矩阵,形状为【batch_size,in_features】
- A A A为权重矩阵,形状为【out_features,in_features】
- b b b为偏置,形状为【out_features】
(二)、函数调用参数解析
首先我们看一下Linear
类的构造函数的函数头部分,然后进一步分析
def __init__(self, in_features: int, out_features: int, bias: bool = True,device=None, dtype=None) -> None:
- in_features
in_features
是我们我们的输入的每个样本的特征数。这样好像不太好理解,我先说明一下,深度学习中,数据的输入往往是以批量的形式输入,那么一次会处理一个批次。
比如一次我要处理100个样本,前面说每个样本有in_features
个特征,因此假设我们一个批次处理的数据量(样本个数)用batch_size
表示,那么我们的输入矩阵(张量)的性状为[batch_size,in_features]
,表示这个矩阵有batch_size行,in_features列。
举个实际的例子吧,我现在要拟合一个3阶多项式,多项式形式为
y
=
a
x
3
+
b
x
2
+
c
x
+
d
y=ax^3+bx^2+cx+d
y=ax3+bx2+cx+d。我将给出n个多项式和其多项式真实的值作为训练集的样本和标签,
[
a
b
c
d
]
\begin{bmatrix}a&b&c&d\end{bmatrix}
[abcd]是我们的模型需要去学习的权重参数。我给出3个样本(batch_size=3)作为例子展示,矩阵如下:
[
8
4
2
1
27
9
3
1
64
16
4
1
]
\begin{bmatrix} 8&4&2&1\\ 27&9&3&1\\ 64&16&4&1 \end{bmatrix}
827644916234111
上述矩阵每一行有4个元素
[
x
3
x
2
x
1
x
0
]
\begin{bmatrix}x^3&x^2&x^1&x^0\end{bmatrix}
[x3x2x1x0]的值,对于不同的行(不同的多项式),
x
x
x我分别取了
[
2
3
4
]
T
\begin{bmatrix}2&3&4\end{bmatrix}^T
[234]T。每一行的4个元素,在深度学习中我们将其称为4个特征。基于上述分析,我们得到上面这个输入的形状为[3,4]
,表示输入了3个样本,每个样本有4个特征。
然后上述矩阵还原其实就是如下几个多项式:
y
1
=
a
⋅
2
3
+
b
⋅
2
2
+
c
⋅
2
+
d
⋅
1
y
2
=
a
⋅
3
3
+
b
⋅
3
2
+
c
⋅
3
+
d
⋅
1
y
3
=
a
⋅
4
3
+
b
⋅
4
2
+
c
⋅
4
+
d
⋅
1
\begin{aligned} y_1&=a\cdot 2^3+b\cdot 2^2+c\cdot 2+d\cdot1\\ y_2&=a\cdot 3^3+b\cdot 3^2+c\cdot 3+d\cdot1\\ y_3&=a\cdot 4^3+b\cdot 4^2+c\cdot 4+d\cdot1 \end{aligned}
y1y2y3=a⋅23+b⋅22+c⋅2+d⋅1=a⋅33+b⋅32+c⋅3+d⋅1=a⋅43+b⋅42+c⋅4+d⋅1
假设真实权重矩阵
A
r
e
a
l
=
[
a
b
c
d
]
A_{real}=\begin{bmatrix}a&b&c&d\end{bmatrix}
Areal=[abcd]为
[
3
5
3.2
7
]
\begin{bmatrix}3&5&3.2&7\end{bmatrix}
[353.27],那么我们将会得到上述输入样本的真实标签
[
y
1
y
2
y
3
]
\begin{bmatrix}y_1&y_2&y_3\end{bmatrix}
[y1y2y3]为
[
58.4000
143.6000
292.8000
]
T
\begin{bmatrix}58.4000&143.6000&292.8000\end{bmatrix}^T
[58.4000143.6000292.8000]T,上述标签由下面的程序计算得到:
import torch
from torch import nn
#创建一个全连接层
connected_layer = nn.Linear(in_features=4, out_features=1, bias=True)
#手动输入3个样本
input_data = torch.tensor(
[
[8, 4, 2, 1],
[27, 9, 3, 1],
[64, 16, 4, 1]
], dtype=torch.float32
)
#手动设置权重,即a,b,c,d的值
connected_layer.weight.data = torch.tensor(
[
[3, 5, 3.2, 7]
], dtype=torch.float32
)
#手动设置偏置b
connected_layer.bias.data = torch.tensor([1],dtype=torch.float32)
#获取输出结果
output = connected_layer(input_data)
print(output)
- out_features
这个参数表示我们的数据通过全连接层计算,得到的结果会有几个特征。对于上面的例子,显然只会有1个特征,就是 y i y_i yi的最终值。
- bias
这参数的汉译名为“偏置”,因此它就是我们计算表达式 y = x A T + b y=xA^T+b y=xAT+b中的b。
(三)、计算解析
上述例子真实标签是怎么得到的?我们按照如下步骤进行
y
=
x
A
T
+
b
=
[
8
4
2
1
27
9
3
1
64
16
4
1
]
[
3
5
3.2
7
]
+
1
=
[
58.4000
143.6000
292.8000
]
\begin{aligned} y&=xA^T+b\\ &=\begin{bmatrix}8&4&2&1\\27&9&3&1\\64&16&4&1\end{bmatrix}\begin{bmatrix}3\\5\\3.2\\7\end{bmatrix}+1\\ &=\begin{bmatrix}58.4000\\143.6000\\292.8000\end{bmatrix} \end{aligned}
y=xAT+b=
827644916234111
353.27
+1=
58.4000143.6000292.8000
这就是全连接层的计算。
三、结语
我写这篇文章的原因是,我之前对权重矩阵的形状在Linear层的表示不是很理解。对于上面的例子,输入形状为【3x4】,在我的理解下,权重矩阵的形状应该为【4x1】因为这样刚好就可以直接进行矩阵乘积了,很方便。
我不明白之处在于为什么Linear的权重矩阵形状要设置为【1,4】,然后运算的时候先做一个转置变成【4x1】,再进行【3x4】【4x1】的矩阵乘积操作。
大家有想法的在评论区留言吧,我在网上暂时没找到解释得过去的答案!