免费的云产品

免费的云产品

云数据库

地址:https://planetscale.com/

使用:直接无脑操作,值得注意的是在选择CLI直接通过命令行来连接数据库时

1
mysql -h lqu8oy8shq8c.us-east-3.psdb.cloud -u 7f2u85345msy -p************ --ssl-mode=VERIFY_IDENTITY --ssl-ca=/etc/ssl/certs/ca-certificates.crt

其中--ssl-mode=VERIFY_IDENTITY --ssl-ca=/etc/ssl/certs/ca-certificates.crt在使用Windows操作时要删掉,这是给Linux用的

然后就能顺利连接

Vercel

地址:https://vercel.com

Vercel是一个开箱即用的网站托管平台,全球都有CDN节点,还是很快的(还可以自定义域名)

在上面可以用github登录,在授权之后该网站可以直接运行github仓库里面的代码

4everland

地址:https://www.4everland.org/

这个产品的作用在我们看来跟vercel很相似,用来部署静态资源(可以自定义域名)

它的功能和Vercel很相似,也有全球CDN加速,不过速度稍微慢一点

netlify

地址:https://www.netlify.com/

这个梗前面两款功能几乎是一样的,不过缺点就是项目部署的速度是在太慢了,七八分钟的时间,不过有个好处就是可以自动帮你把http变成https,不用去惨兮兮的申请一年的证书了

另外,有一款很不错的Hexo主题,文档如下

https://www.notion.so/Miracle-49659de5b1764e908c7496418f06277e


为git设置代理

为给git设置代理

通过软件形式为git设置代理

命令(端口改为自己的端口):

1
2
git config --global https.proxy http://127.0.0.1:1083
git config --global https.proxy https://127.0.0.1:1083

当切换代理端口,需要取消代理时候

1
2
git config --global --unset http.proxy
git config --global --unset https.proxy

邮件发送

经常有些要发送邮件的需求,但是去网上拷代码老是拷不到能直接运行的,还经常要去以前的项目里面拷,今天直接发出来算了,免得每次都要去别的项目拷。

(只支持发送简单的文本文件,发附件的稍微复杂一丢丢,这里就不贴出来了)

依赖:

1
2
3
4
5
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.5.0-b01</version>
</dependency>

源代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
import javax.mail.*;
import javax.mail.internet.*;
import java.util.Properties;

public class SendIdentifyingCode {

public SendIdentifyingCode(String address, String content) {
Properties prop = new Properties();
prop.setProperty("mail.host", "smtp.qq.com");
prop.setProperty("mail.transport.protocol", "smtp");
prop.setProperty("mail.smtp.auth", "true");
//用465端口
prop.setProperty("mail.smtp.port", "465");
prop.setProperty("mail.smtp.ssl.enable", "true");

Session session = Session.getInstance(prop);
session.setDebug(true);
Transport ts = null;
try {
ts = session.getTransport();
//下面这行自己用自己的噢,最后那个参数在我下面那个图里面开启点两下就可以找到了
ts.connect("smtp.qq.com", "ohgreenorangestack@foxmail.com", "xxxxxxxxxxx");
Message message = createSimpleMail(session, address, content);
ts.sendMessage(message, message.getAllRecipients());
} catch (MessagingException e) {
e.printStackTrace();
} finally {
try {
assert ts != null;
ts.close();
} catch (MessagingException e) {
e.printStackTrace();
}
}
}

private MimeMessage createSimpleMail(Session session, String address, String content) throws MessagingException {
MimeMessage message = new MimeMessage(session);
message.setFrom(new InternetAddress("ohgreenorangestack@foxmail.com"));
message.setRecipient(Message.RecipientType.TO, new InternetAddress(address));
message.setSubject("FaceProcessor验证消息");//邮件标题
message.setContent(content, "text/html;charset=UTF-8");//支持html格式
return message;
}

}

这边用的QQ邮箱

端口报错的话就用25,但是云服务器不让你用25端口,465端口是用的SSL,用这端口屁事比较多,jdk版本稍微高一点的话把SSL禁用了

解决方案https://blog.csdn.net/ooblack/article/details/119300423

然后要是能用25的话尽量用25得了,但是云服务器去申请25解封通过率不高

贴一个各个邮箱对应的端口

https://www.cnblogs.com/ni-huang-feng-wu/p/14773917.html

SpringBoot又把这玩意又封装了一次,如果是Spring忠实粉丝的话可以去用SpringBoot的

1
2
3
4
5
6
7
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-mail -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
<version>2.5.6</version>
</dependency>

接下来怎么写自行百度。


IDEA安装JavaFx
  1. jdk11之后jdk就不内置javafx了,需要自己下载
  1. 在idea中新建JavaFx项目:

  2. 创建成功后发现代码标红

  3. 这个时候要把刚刚下载的JavaFx包解压后添加进去

  4. 选择到自己解压的路径的文件的lib包,如然后确定

  5. 然后发现没有标红报错了,但是代码不能运行,显示”错误: 缺少 JavaFX 运行时组件, 需要使用该组件来运行此应用程序“

  6. 此时再配置一下下VMoption就行

这一步可能随idea版本不同而不同,但是没关系,找到VMoption就行

输入:

--module-path="D:\program files\java\javafx-sdk-11.0.2\lib" --add-modules=javafx.controls,javafx.fxml

将上面D:\program files\java\javafx-sdk-11.0.2\lib替换成你自己的解压路径就行。

不过如果你没有这个sample.fxml文件,也就是你不是创建的JavaFx项目,你创建的是一个普通的JavaSE项目,你用这个项目来写JavaFX,就应该加入的参数是--module-path="D:\program files\java\javafx-sdk-11.0.2\lib" --add-modules=javafx.controls

参数加入完毕之后点击apply,然后就可以正常运行了


卷积神经网络案例分析

简单分析一下主流的几种神经网络

LeNet

LetNet作为卷积神经网络中的HelloWorld,它的结构及其的简单,1998年由LeCun提出

基本过程:

可以看到LeNet-5跟现有的conv->pool->ReLU的套路不同,它使用的方式是conv1->pool->conv2->pool2再接全连接层,但是不变的是,卷积层后紧接池化层的模式依旧不变。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import torch.nn as nn
import torch


class LeNet(nn.Module):
def __init__(self):
super(LeNet, self).__init__()

layer1 = nn.Sequential()
# Convolution with 5x5 kernel+2padding:28×28×6
layer1.add_module('conv1', nn.Conv2d(1, 6, kernel_size=(3, 3), padding=1))
# Pool with 2x2 average kernel+2 stride:14×14×6
layer1.add_module('pool1', nn.MaxPool2d(kernel_size=2))
self.layer1 = layer1

layer2 = nn.Sequential()
# Convolution with 5x5 kernel (no pad):10×10×16
layer2.add_module('conv2', nn.Conv2d(6, 16, kernel_size=(5, 5)))
# Pool with 2x2 average kernel+2 stride: 5x5×16
layer2.add_module('pool2', nn.MaxPool2d(kernel_size=2))
self.layer2 = layer2

layer3 = nn.Sequential()
# 5 = ((28/2)-4)/2
layer3.add_module('fc1', nn.Linear(16 * 5 * 5, 120))
layer3.add_module('fc2', nn.Linear(120, 84))
layer3.add_module('fc3', nn.Linear(84, 10))
self.layer3 = layer3

def forward(self, x):
x = self.layer1(x)
# print(x.size())
x = self.layer2(x)
# print(x.size())
# 展平x
x = torch.flatten(x, 1)
x = self.layer3(x)
return x


# 测试
test_data = torch.rand(1, 1, 28, 28)
model = LeNet()
model(test_data)

输出

1
2
tensor([[ 0.0067, -0.0431,  0.1072,  0.1275,  0.0143,  0.0865, -0.0490, -0.0936,
-0.0315, -0.0367]], grad_fn=<AddmmBackward0>)

AlexNet

这个图看起来稍微可能有亿点复杂,其实这个是因为当时的GPU计算能力不太行,而Alex又比较复杂,所以Alex使用了两个GPU并行来做运算,现在已经完全可以用一个GPU来代替了。
相对于LeNet来说,Alex网络层数更深,同时第一次引入了激活层ReLU,又在全连接层引入了Dropout层防止过拟合。
执行流程图在上面,跟LeNet的执行流程图放在一张图上。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
import torch.nn as nn
import torch


class AlexNet(nn.Module):
def __init__(self, num_class):
super(AlexNet, self).__init__()
self.features = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=(11, 11), stride=(4, 4)),
nn.ReLU(True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(64, 192, kernel_size=(5, 5), padding=2),
nn.ReLU(True),
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(192, 384, kernel_size=(3, 3), padding=1),
nn.ReLU(True),
nn.Conv2d(384, 256, kernel_size=(3, 3), padding=1),
nn.ReLU(True),
nn.Conv2d(256, 256, kernel_size=(3, 3), padding=1),
nn.ReLU(True),
nn.MaxPool2d(kernel_size=3, stride=2),
)

self.classifier = nn.Sequential(
nn.Dropout(),
nn.Linear(256*5*5, 4096),
nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, 4096),
nn.ReLU(True),
nn.Linear(4096, num_class)
)

def forward(self, x):
x = self.features(x)
print(x.size())
x = x.view(x.size(0), 256 * 5 * 5)
x = self.classifier(x)
return x

# 测试
test_data = torch.rand(1, 3, 224, 224)
model = AlexNet(10)
model(test_data)

输出:

1
2
3
torch.Size([1, 256, 5, 5])
tensor([[-0.0044, 0.0114, 0.0032, -0.0099, 0.0035, -0.0024, 0.0103, -0.0194,
0.0149, 0.0094]], grad_fn=<AddmmBackward0>)

VggNet

VggNet是ImageNet 2014年的亚军,总的来说就是它使用了更小的滤波器,用了更深的结构来提升深度学习的效果,从图里面可以看出来这一点,它没有使用11*11这么大的滤波器,取而代之的使用的都是3*3这种小的滤波器,它之所以使用很多小的滤波器,是因为层叠很多小的滤波器的感受野和一个大的滤波器的感受野是相同的,还能减少参数。

代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
import torch.nn as nn


class VGG(nn.Module):
def __init__(self, num_class):
super(VGG, self).__init__()

self.features = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=(3, 3), padding=1),
nn.ReLU(True),
nn.Conv2d(64, 64, kernel_size=(3, 3), padding=1),
nn.ReLU(True),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(64, 128, kernel_size=(3, 3), padding=1),
nn.ReLU(True),
nn.Conv2d(128, 128, kernel_size=(3, 3), padding=1),
nn.ReLU(True),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(128, 256, kernel_size=(3, 3), padding=1),
nn.ReLU(True),
nn.Conv2d(256, 256, kernel_size=(3, 3), padding=1),
nn.ReLU(True),
nn.Conv2d(256, 256, kernel_size=(3, 3), padding=1),
nn.ReLU(True),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(256, 512, kernel_size=(3, 3), padding=1),
nn.ReLU(True),
nn.Conv2d(512, 512, kernel_size=(3, 3), padding=1),
nn.ReLU(True),
nn.Conv2d(512, 512, kernel_size=(3, 3), padding=1),
nn.ReLU(True),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(512, 512, kernel_size=(3, 3), padding=1),
nn.ReLU(True),
nn.Conv2d(512, 512, kernel_size=(3, 3), padding=1),
nn.ReLU(True),
nn.Conv2d(512, 512, kernel_size=(3, 3), padding=1),
nn.ReLU(True),
nn.MaxPool2d(kernel_size=2, stride=2),
)
self.classifier = nn.Sequential(
nn.Linear(512 * 7 * 7, 4096), nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, 4096), nn.ReLU(True),
nn.Dropout(),
nn.Linear(4096, num_class),
)
self._initialize_weights()

def forward(self, x):
x = self.features(x)
x = x.view(x.size(0), -1)
x = self.classifier(x)
return x

使用卷积神经网络实现对Minist数据集的预测

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import matplotlib.pyplot as plt
import torch.utils.data
import torchvision.datasets
import os
import torch.nn as nn
from torchvision import transforms


class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.layer1 = nn.Sequential(
nn.Conv2d(1, 16, kernel_size=(3, 3)),
nn.BatchNorm2d(16),
nn.ReLU(inplace=True),
)

self.layer2 = nn.Sequential(
nn.Conv2d(16, 32, kernel_size=(3, 3)),
nn.BatchNorm2d(32),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2),
)

self.layer3 = nn.Sequential(
nn.Conv2d(32, 64, kernel_size=(3, 3)),
nn.BatchNorm2d(64),
nn.ReLU(inplace=True)
)

self.layer4 = nn.Sequential(
nn.Conv2d(64, 128, kernel_size=(3, 3)),
nn.BatchNorm2d(128),
nn.ReLU(inplace=True),
nn.MaxPool2d(kernel_size=2, stride=2)
)

self.fc = nn.Sequential(
nn.Linear(128 * 4 * 4, 1024),
nn.ReLU(inplace=True),
nn.Linear(1024, 128),
nn.Linear(128, 10)
)

def forward(self, x):
x = self.layer1(x)
x = self.layer2(x)
x = self.layer3(x)
x = self.layer4(x)
x = x.view(x.size(0), -1)
x = self.fc(x)
return x



os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"

data_tf = transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize([0.5], [0.5])]
)

train_dataset = torchvision.datasets.MNIST(root='F:/机器学习/pytorch/书/data/mnist', train=True,
transform=data_tf, download=True)

test_dataset = torchvision.datasets.MNIST(root='F:/机器学习/pytorch/书/data/mnist', train=False,
transform=data_tf, download=True)

batch_size = 100
train_loader = torch.utils.data.DataLoader(
dataset=train_dataset, batch_size=batch_size
)

test_loader = torch.utils.data.DataLoader(
dataset=test_dataset, batch_size=batch_size
)

model = CNN()
model = model.cuda()
criterion = nn.CrossEntropyLoss()
criterion = criterion.cuda()
optimizer = torch.optim.Adam(model.parameters())

# 节约时间,三次够了
iter_step = 3
loss1 = []
loss2 = []
for step in range(iter_step):
loss1_count = 0
loss2_count = 0
for images, labels in train_loader:
images = images.cuda()
labels = labels.cuda()
images = images.reshape(-1, 1, 28, 28)
output = model(images)
pred = output.squeeze()

optimizer.zero_grad()
loss = criterion(pred, labels)
loss.backward()
optimizer.step()

_, pred = torch.max(pred, 1)

loss1_count += int(torch.sum(pred == labels)) / 100
# 测试
else:
test_loss = 0
accuracy = 0
with torch.no_grad():
for images, labels in test_loader:
images = images.cuda()
labels = labels.cuda()
pred = model(images.reshape(-1, 1, 28, 28))
_, pred = torch.max(pred, 1)
loss2_count += int(torch.sum(pred == labels)) / 100

loss1.append(loss1_count / len(train_loader))
loss2.append(loss2_count / len(test_loader))

print(f'第{step}次训练:训练准确率:{loss1[len(loss1)-1]},测试准确率:{loss2[len(loss2)-1]}')

plt.plot(loss1, label='Training loss')
plt.plot(loss2, label='Validation loss')
plt.legend()

输出:

1
2
3
4
第0次训练:训练准确率:0.9646166666666718,测试准确率:0.9868999999999996
第1次训练:训练准确率:0.9865833333333389,测试准确率:0.9908999999999998
第2次训练:训练准确率:0.9917000000000039,测试准确率:0.9879999999999994
<matplotlib.legend.Legend at 0x21f03092fd0>


nn.Conv2d()中dilation参数的作用

nn.Conv2d()中dilation参数的作用

下面这张图很好的描述了这个参数的作用

优点:

这样每次进行单次计算时覆盖的面积(感受域)增大,最开始时3*3 = 9 然后是5*5 = 25最后是7*7=49,增加了感受域却并未增加计算量,保留了更多的细节信息,对图像还原有明显的提升。


线性回归学习总结及其入门案例

线性回归

最小二乘法

使用torch.lstsq()求解线性回归问题

两个重要推论

  1. 与权值点乘

X[i,:]W=x[i,0]w[0]+x[i,1]w[1]++x[i,m1]w[m1]+x[i,m]w[m]X[i, :]·W = x[i, 0]w[0] + x[i, 1]w[1] + …… + x[i, m-1]w[m-1] + x[i, m]w[m]

  1. 二范数的计算方法

YXW22=i=0n1(y[i]X[i,:]W)|| Y - X · W ||_2^2 = \sum_{i=0}^{n-1}(y[i] - X[i, : ] · W)

  1. 误差表达式

ζ(W;X,Y)=1nYXW22\zeta(W; X, Y) = {1\over n} || Y - X · W ||_2^2

1
2
3
4
5
6
7
import torch
x = torch.tensor([[1., 1., 1.], [2., 3., 1.], [3., 5., 1.], [4., 2., 1.], [5., 4., 1.]])
y = torch.tensor([-10., 12., 14., 16., 18.])
wr, _ = torch.lstsq(y, x)
w = wr[:3]
print(wr)
print(w)
1
2
3
4
5
6
7
8
tensor([[  4.6667],
[ 2.6667],
[-12.0000],
[ 10.0885],
[ 2.2110]])
tensor([[ 4.6667],
[ 2.6667],
[-12.0000]])

几种损失函数

MSE损失函数

求出来的就是目标值与预测值的差的平方和
公式

MSE=1ni=1n(yiyip)2MSE = {1 \over n}\sum_{i=1}^n(y_i - y_i ^p)^2

  • 优点:各点都连续光滑,方便求导,具有较为稳定的解
  • 缺点:不够鲁棒,函数的输入值距离中心值较远的时候,使用梯度下降法求解的时候梯度很大,可能导致梯度爆炸
  • pytorch对应的类torch.nn.MSELoss

MAE损失函数

求出来的就是目标值与预测值差的绝对值的和
公式

MAE=1ni=1nyiyipMAE = {1 \over n}\sum_{i=1}^n|y_i - y_i ^p|

  • 优点:无论对于什么样的输入值,都有着稳定的梯度,不会导致梯度爆炸问题,具有较为稳健性的解
  • 缺点:在中心点是折点,不能求导,不方便求解

L1损失函数

L1范数损失函数,也被称为最小绝对值偏差(LAD),最小绝对值误差(LAE)。总的说来,它是把目标值(Yi)与估计值(f(xi))的绝对差值的总和(S)最小化:
公式

L1=i1nYif(xi)L1 = \sum_{i-1}^n|Y_i - f(x_i)|

  • pytorch中对应的类torch.nn.L1Loss

L2损失函数

L2范数损失函数,也被称为最小平方误差(LSE)。总的来说,它是把目标值(Yi)与估计值(f(xi))的差值的平方和(S)最小化:
公式

L2=i1n(Yif(xi))2L2 = \sum_{i-1}^n(Y_i - f(x_i))^2

L1损失函数与L2损失函数的优缺点与前面MSE损失函数和MAS损失函数的优缺点是互通的

smooth L1损失函数

综合起来比对,我们发现要是我们能解决L1损失函数的折点给弄掉,让它可导就好了,所以就出现了smooth L1损失函数
公式

SmoothL1(x)={0.5x2x<1x0.5x1 {Smooth_L}_1(x) = \begin{cases} 0.5x^2 & |x| < 1 \\ |x| - 0.5 & |x| \geq 1 \end{cases}

  • 优点:该函数实际上就是一个分段函数,在[-1,1]之间实际上就是L2损失,这样解决了L1的不光滑问题,在[-1,1]区间外,实际上就是L1损失,这样就解决了离群点梯度爆炸的问题。而且Smooth L1 Loss 结合了 L1 和 L2 的优点:早期使用 L1,梯度稳定,快速收敛,后期使用 L2,逐渐收敛到最优解。
  • pytorch中对应的类torch.nn.SmoothL1Loss
1
2
3
4
5
6
7
8
9
10
# 一个调用MSE损失函数的例子

#实例化该类
criterion = torch.nn.MSELoss()
pred = torch.arange(5, dtype=torch.float32,requires_grad=True)
y = torch.ones(5)
loss = criterion(pred, y)
print(loss)
loss.backward()
# print(loss.grad)

输出:

1
tensor(3., grad_fn=<MseLossBackward0>)

使用优化器求解线性回归

不管是什么损失函数,我们总是可以用梯度下降法找到合适的权重W,使得损失最小,采用这种方法的话我们需要先实现损失,然后对损失求梯度,并以此更新权重W的值,但是即使是最简单的MSE损失函数,当数据过多而不能一次性全部载入内存时,我们可以采用随机梯度下降法,在每次运行迭代时选择一部分数据进行运算。
下面这个例子实现和开头那个例子一样的结果,但是这个方法更加的费事费力,花费的时间也是更加多,所以如果能用tourch.lstsq()就用tourch.lstsq()方法,真不能用tourch.lstsq()方法了(比如损失不是MSE损失或者数据太多没办法一下子全部载入内存),我们才选用这个方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import torch
import torch.nn
import torch.optim

x = torch.tensor([[1., 1., 1.], [2., 3., 1.], [3., 5., 1.], [4., 2., 1.], [5., 4., 1.]], device='cuda')
y = torch.tensor([-10., 12., 14., 16., 18.], device='cuda')
w = torch.zeros(3, requires_grad=True, device='cuda')

criterion = torch.nn.MSELoss()
optimizer = torch.optim.Adam([w, ], )

for step in range(30001):
if step:
optimizer.zero_grad() # 清零
loss.backward() # 求梯度
optimizer.step() # 根据梯度更新自变量

pred = torch.mv(x, w) # 矩阵乘法
loss = criterion(pred, y)
if step % 5000 == 0:
print('step = {} loss = {:g} W = {}'.format(step, loss, w.tolist()))

输出:

1
2
3
4
5
6
7
step = 0 loss = 204 W = [0.0, 0.0, 0.0]
step = 5000 loss = 40.8731 W = [2.3051974773406982, 1.712536334991455, -0.6180324554443359]
step = 10000 loss = 27.9001 W = [3.6783804893493652, 1.7130744457244873, -5.2205023765563965]
step = 15000 loss = 22.31 W = [4.292291641235352, 2.293663263320923, -9.385353088378906]
step = 20000 loss = 21.3341 W = [4.655962944030762, 2.6559813022613525, -11.925154685974121]
step = 25000 loss = 21.3333 W = [4.666664123535156, 2.666663885116577, -12.0]
step = 30000 loss = 21.3333 W = [4.666667938232422, 2.666668176651001, -11.999998092651367]

使用torch.nn.Linear()实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import torch
import torch.nn
import torch.optim

x = torch.tensor([[1., 1., 1.], [2., 3., 1.], [3., 5., 1.], [4., 2., 1.], [5., 4., 1.]])
y = torch.tensor([-10., 12., 14., 16., 18.])

fc = torch.nn.Linear(3, 1)
criterion = torch.nn.MSELoss()
optimizer = torch.optim.Adam(fc.parameters())

weights, bias = fc.parameters()
fc(x)
for step in range(30001):
if step:
optimizer.zero_grad()
loss.backward()
optimizer.step()

pred = fc(x)
loss = criterion(pred, y)
if step % 5000 == 0:
print('step = {} loss = {:g} W = {}, bias = {}'.format(step, loss, weights[0, :].tolist(), bias.item()))

输出:

1
2
3
4
5
6
7
<generator object Module.parameters at 0x000001ED118B8270>
step = 5000 loss = 106.462 W = [0.4140699803829193, 0.7813165187835693, 2.938326358795166], bias = 2.9747958183288574
step = 10000 loss = 104 W = [0.007105899043381214, 0.007294247858226299, 4.956961631774902], bias = 4.993431568145752
step = 15000 loss = 104 W = [2.2107651602709666e-06, 2.068739377136808e-06, 4.981757640838623], bias = 5.018227577209473
step = 20000 loss = 104 W = [2.710844455577899e-07, 2.585106244623603e-07, 4.981764793395996], bias = 5.018234729766846
step = 25000 loss = 104 W = [-4.070022259838879e-05, -4.075446486240253e-05, 4.981725215911865], bias = 5.018195152282715
step = 30000 loss = 104 W = [1.3781600500806235e-06, 1.4800637018197449e-06, 4.981767177581787], bias = 5.018237113952637

数据的归一化

为什么要归一化?

在一些线性规划问题中,特征数值范围和标签的数值范围差别很大,或者不同特征之间的数值范围差别很大。这时,某些权重值可能会特别大,这为优化器学习这些权重值带来了困难。

如何归一化?

将特征A归一化
mean(A)为A的平均值,std(A)的方差

Anorm=Amean(A)std(A)A_{norm} = {A-mean(A) \over std(A)}

归一化之后的数据特征是什么?

归一化之后的数据均值为0,方差为1

代码示例:

  • 未进行归一化的代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import torch.nn
import torch.optim

x = torch.tensor([[1000000, 0.0001], [2000000, 0.0003], [3000000, 0.0005], [4000000, 0.0002], [5000000, 0.0004]], device="cuda")
y = torch.tensor([-1000., 1200., 1400., 1600., 1800.], device='cuda').reshape(-1, 1)

fc = torch.nn.Linear(2, 1)
fc = fc.cuda()
# 得出当前权值所计算出来的结果
pred = fc(x)
print(pred)
criterion = torch.nn.MSELoss()
criterion = criterion.cuda()
optimizer = torch.optim.Adam(fc.parameters())

for step in range(100001):
if step:
optimizer.zero_grad()
loss.backward()
optimizer.step()
pred = fc(x)
loss = criterion(pred, y)
if step % 10000 == 0:
print('step = {}, loss = {:g}'.format(step, loss))

输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
tensor([[ 580872.8750],
[1161746.1250],
[1742619.3750],
[2323492.5000],
[2904365.7500]], device='cuda:0', grad_fn=<AddmmBackward0>)
step = 0, loss = 3.70667e+12
step = 10000, loss = 436096
step = 20000, loss = 435005
step = 30000, loss = 432516
step = 40000, loss = 430062
step = 50000, loss = 427641
step = 60000, loss = 425254
step = 70000, loss = 432383
step = 80000, loss = 420584
step = 90000, loss = 418410
step = 100000, loss = 416046

可以发现这个速度太慢了,迭代一万次损失值还是很高,所以下面就把数据归一化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import torch
import torch.nn
import torch.optim

x = torch.tensor([[1000000, 0.0001], [2000000, 0.0003], [3000000, 0.0005], [4000000, 0.0002], [5000000, 0.0004]])
y = torch.tensor([-1000., 1200., 1400., 1600., 1800.]).reshape(-1, 1)

x_mean, x_std = torch.mean(x, dim=0), torch.std(x, dim=0)
x_norm = (x - x_mean) / x_std

y_mean, y_std = torch.mean(y, dim=0), torch.std(y, dim=0)
y_norm = (y - y_mean) / y_std

fc = torch.nn.Linear(2, 1)
# 得出当前权值所计算出来的结果
pred = fc(x)
print(pred)
criterion = torch.nn.MSELoss()
optimizer = torch.optim.Adam(fc.parameters())

for step in range(10001):
if step:
optimizer.zero_grad()
loss_norm.backward()
optimizer.step()
pred_norm = fc(x_norm)
loss_norm = criterion(pred_norm, y_norm)
# 数据还原
pred = pred_norm * y_std + y_mean
loss = criterion(pred, y)
if step % 1000 == 0:
print('step = {}, loss = {:g}'.format(step, loss))

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
tensor([[ -599029.2500],
[-1198058.6250],
[-1797088.0000],
[-2396117.5000],
[-2995146.7500]], grad_fn=<AddmmBackward0>)
steop = 0, loss = 4.38259e+06
steop = 1000, loss = 654194
steop = 2000, loss = 224888
steop = 3000, loss = 213705
steop = 4000, loss = 213341
steop = 5000, loss = 213333
steop = 6000, loss = 213333
steop = 7000, loss = 213333
steop = 8000, loss = 213333
steop = 9000, loss = 213333
steop = 10000, loss = 213333

实战

用最小二乘法对世界人口做线性回归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import os
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
# 上面两行忽略,不然可能会报警告
import torch
import pandas as pd
url = "https://zh.wikipedia.org/wiki/%E4%B8%96%E7%95%8C%E4%BA%BA%E5%8F%A3"
# 从维基百科获取数据
df = pd.read_html(url, header=0, attrs={"class": "wikitable"}, encoding="utf8")[0]
# print(df)
world_populations = df.copy().iloc[18:31, [0, 1]]

# 要是访问不了维基百科,数据点击 https://oss.xuziao.cn/blogdata/%E6%B5%8B%E8%AF%95%E6%95%B0%E6%8D%AE.csv 下载
# world_populations.to_csv('测试数据.csv')

# 把年对应的列转换为张量
years = torch.tensor(world_populations.iloc[:, 0].values.astype(float), dtype=torch.float32)
# 把人口对应的列转换为张量
populations = torch.tensor(world_populations.iloc[:, 1].values.astype(float), dtype=torch.float32)

# 变成[[年份,1], [年份,1], .....]的形式,矩阵相乘时就会时w1 * 年份 + w2 * 1的样式
x = torch.stack([years, torch.ones_like(years)], 1)

y = populations

# 使用最小二乘法
wr, _ = torch.lstsq(y, x)
# print(wr)
# 获取前两位(即w1, w2)
slope, intercept = wr[:2, 0]
result = 'population = {:.2e}*year {:.2e}'.format(slope, intercept)
print('回归结果:'+result)

# 绘图
import matplotlib.pyplot as plt
plt.scatter(years, populations, s = 7, c='blue', marker='o')
estimates = [slope * yr + intercept for yr in years]
plt.plot(years, estimates, c='red')
plt.xlabel('Year')
plt.ylabel('Population')
plt.show()

输出:

1
回归结果:population = 7.43e+03*year -1.43e+07

从上面的图像可以看出,拟合的效果是相当不错的,下面试一试使用Adam优化器进行线性回归

使用Adam优化器进行线性回归

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import os
os.environ["KMP_DUPLICATE_LIB_OK"] = "TRUE"
# 上面两行忽略,不然可能会报警告
import pandas as pd
import torch
url = "https://zh.wikipedia.org/wiki/%E4%B8%96%E7%95%8C%E4%BA%BA%E5%8F%A3"
# 从维基百科获取数据
df = pd.read_html(url, header=0, attrs={"class": "wikitable"}, encoding="utf8")[0]
# print(df)
world_populations = df.copy().iloc[18:31, [0, 1]]

# 要是访问不了维基百科,数据点击 https://oss.xuziao.cn/blogdata/%E6%B5%8B%E8%AF%95%E6%95%B0%E6%8D%AE.csv 下载
# world_populations.to_csv('测试数据.csv')

# 把年对应的列转换为张量
years = torch.tensor(world_populations.iloc[:, 0].values.astype(float), dtype=torch.float32)
# 把人口对应的列转换为张量
populations = torch.tensor(world_populations.iloc[:, 1].values.astype(float), dtype=torch.float32)

# 以上代码是复制的上一节,就没有什么好看的了


import torch.nn
import torch.optim

x = years.reshape(-1, 1)
# print(x)
y = populations
# 下面进行数据的归一化,可以看出这数据量级差别较大,进行数据归一化可以快速下降

x_mean, x_std = torch.mean(x, dim=0), torch.std(x, dim=0)
x_norm = (x - x_mean) / x_std

y_mean, y_std = torch.mean(y, dim=0), torch.std(y, dim=0)
y_norm = (y - y_mean) / y_std

# 一个输入一个输出 会随机生成一个1*1的矩阵
fc = torch.nn.Linear(1, 1)
# MSE损失函数
criterion = torch.nn.MSELoss()
# 创建优化器
optimizer = torch.optim.Adam(fc.parameters())
# 浅拷贝?
weights_norm, bias_norm = fc.parameters()

for step in range(6001):
if step:
# 权值清零
fc.zero_grad()
# 计算梯度
loss_norm.backward()
# 更新权值(fc里面的一些属性)
optimizer.step()
# 矩阵乘法,即获取输出(归一化之后的输出,此例中但凡有个_norm后缀的都是归一化之后的值)
output_norm = fc(x_norm)
# 去掉所有维度为一的维度
pred_norm = output_norm.squeeze()
# 通过MSE损失函数计算损失值
loss_norm = criterion(pred_norm, y_norm)
# 通过归一化之后的权重计算原数据权重,这个公式跟下面那个公式皆由高等数学推出
weights = y_std / x_std * weights_norm
# 通过归一化之后的偏移量得到原数据的偏移量
bias = (weights_norm * (0 - x_mean) / x_std + bias_norm) * y_std + y_mean
if step % 1000 == 0:
print('第{}步:weight = {}, bias = {}'.format(step, weights.item(), bias.item()))

# 绘图
import matplotlib.pyplot as plt
plt.scatter(years, populations, s = 7, c='blue', marker='o')
estimates = [weights * yr + bias for yr in years]
plt.plot(years, estimates, c='red')
plt.xlabel('Year')
plt.ylabel('Population')
plt.show()

输出:

1
2
3
4
5
6
7
第0步:weight = -4349.91064453125, bias = 9026279.0
第1000步:weight = 1948.0953369140625, bias = -3404077.75
第2000步:weight = 5750.35400390625, bias = -10932547.0
第3000步:weight = 7200.87255859375, bias = -13804574.0
第4000步:weight = 7425.09765625, bias = -14248540.0
第5000步:weight = 7432.94873046875, bias = -14264084.0
第6000步:weight = 7432.95751953125, bias = -14264102.0

附上张量构造方法:

函数名 张量中的元素内容
torch.tensor() 内容为传入的数据
torch.zeros()、torch.zeros_like() 各元素全为0
torch.ones()、torch.ones_like() 各元素全为1
torch.full()、torch.full_like() 全元素全为指定的值
torch.empty()、torch.empty_like() 未指定元素的值
torch.eye() 主对角线为1,其它为0
torch.arange()、torch.range()、torch.linspace() 各元素等差
torch.logspace() 各元素等比
torch.rand()、torch.rand_like() 各元素独立服从标准均匀分布
torch.randn()、torch.randn_like()、torch.normal() 各元素独立服从标准正态分布
torch.randint()、torch.randint_like() 各元素独立服从离散均匀分布
torch.bernoulli() {0, 1}上的两点分布
torch.multinomial() {0, 1, ……, n-1}上的多点均匀分布
torch.randperm() 各元素为(0, 1, ……, n-1)的一个随机排列