DL基础补全计划(四)---对抗过拟合:权重衰减、Dropout
PS:要转载请注明出处,本人版权所有。
PS: 这个只是基于《我自己》的理解,
如果和你的原则及想法相冲突,请谅解,勿喷。
前置说明
本文作为本人csdn blog的主站的备份。(BlogID=108)
环境说明
- Windows 10
- VSCode
- Python 3.8.10
- Pytorch 1.8.1
- Cuda 10.2
前言
在《DL基础补全计划(三)---模型选择、欠拟合、过拟合》( https://blog.csdn.net/u011728480/article/details/118881125 )一文中,我们已经了解了我们训练过程中的一些现象,对于欠拟合问题我们一般是增加训练集大小。对于过拟合,我们也提供了一个解决方案,那就是限制模型的复杂度(参数个数)。
在以后我们构造的模型里面,其参数都是成百上千的,如果通过限制参数(更换模型)来解决过拟合问题太简单粗暴了,于是我们需要一些更加细腻的手段来抑制过拟合。此外,我们还必须知道,我们增加训练集总是能够缓解过拟合的问题,但是这不是根本办法。于是深度学*大佬们又引出了其他的两个方案,他们分别是权重衰减和Dropout。
到此为止,我们可以有如下的方法缓解过拟合问题:
- 控制模型复杂度
- 增大训练集
- 权重衰减
- Dropout
权重衰减(L2正则化)
其实书上对于权重衰减的定义还是比较好理解的,我们定义我们的模型为M(X),当M(X)=0时,此时,我们的模型最简单,因为所有的输入都是0。我们最终想要的是M(X)的值越来越接*于0,那么我们权重的范数也需要越来越接*于0,这样M(X)的复杂度越来越小。
出于以上的目的,我们可以将权重的范数给加到Loss函数结果里面去,通过BP算法,这样我们可以让权重的范数越来越接*于0。其中我们常用的L2范数,其可以限制权重中的大值,也可以使权重均匀分布,不会出现极端值,这是一般情况下我们想看到的。
下面,我们通过一个实例及图示来感性的认知权重衰减。
实例代码
首先我们设计了一个200个权重和1个偏置的数据生成器,加上噪声,得到我们数据集。这里我们采集了100个测试集和20个训练集。从这里我们马上就可以知道,这个肯定会过拟合,因为训练集太小了,而模型复杂度太大了。
下面是完整代码:
import torch
from torch import nn
import numpy as np
import matplotlib.pyplot as plt
from torch.utils import data
from matplotlib.pyplot import MultipleLocator
fig, ax = plt.subplots()
xdata0, ydata0 = [], []
xdata1, ydata1 = [], []
line0, = ax.plot([], [], 'r-', label='TrainError')
line1, = ax.plot([], [], 'b-', label='TestError')
def init_and_show():
ax.set_xlabel('epoch')
ax.set_ylabel('loss')
ax.set_title('Train/Test Loss')
ax.set_xlim(0, epochs)
ax.set_ylim(10e-5, 100)
ax.set_yscale('log')
# y_locator = MultipleLocator(0.1)
# ax.yaxis.set_major_locator(y_locator)
ax.legend([line0, line1], ('TrainError', 'TestError'))
# ax.legend([line1], ('TestError', ))
line0.set_data(xdata0, ydata0)
line1.set_data(xdata1, ydata1)
plt.show()
def l2_penalty(w, b = None):
if b == None:
return (w**2).sum() / 2
else:
return ((w**2).sum() + (b**2).sum()) / 2
def synthetic_data(true_w, true_b, num_examples): #@save
"""⽣成y = ax1 + bx2 + cx3 .... .... + b + 噪声。"""
X = np.random.normal(0, 1, (num_examples, len(true_w)))
y = np.matmul(X, true_w) + true_b
# 噪声
y += np.random.normal(0, 0.1, y.shape)
return X, y.reshape((-1, 1))
class TestNet(nn.Module):
def __init__(self, input_nums):
super(TestNet, self).__init__()
self.test_net = nn.Sequential(
# y=X*W+B
# x.shape(batch_size, input_nums), w.shape(input_nums, output_nums)
# y.shape(batch_size, output_nums)
torch.nn.Linear(input_nums, 1)
)
def forward(self, x):
# print(x.dtype)
#
return self.test_net(x)
# copy from d2l/torch.py
def load_array(data_arrays, batch_size, is_train=True):
"""Construct a PyTorch data iterator."""
dataset = data.TensorDataset(*data_arrays)
return data.DataLoader(dataset, batch_size, shuffle=is_train)
# def data_loader(batch_size, features, labels):
# num_examples = len(features)
# indices = list(range(num_examples))
# np.random.shuffle(indices) # 样本的读取顺序是随机的
# for i in range(0, num_examples, batch_size):
# j = np.array(indices[i: min(i + batch_size, num_examples)])
# yield torch.tensor(features.take(j, 0)), torch.tensor(labels.take(j)) # take函数根据索引返回对应元素
def train(dataloader, model, loss_fn, optimizer, lambda_val = 3):
size = train_examples
num_batches = train_examples / batch_size
train_loss_sum = 0
for batch, (X, y) in enumerate(dataloader):
# move X, y to gpu
if torch.cuda.is_available():
X = X.to('cuda', dtype=torch.float32)
y = y.to('cuda', dtype=torch.float32)
# Compute prediction and loss
pred = model(X)
param_iter = model.parameters()
# loss = loss_fn(pred, y) + lambda_val*l2_penalty(next(param_iter), next(param_iter))
# next(model.parameters())
loss = loss_fn(pred, y) + lambda_val*l2_penalty(next(model.parameters()))
# Backpropagation
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_loss_sum += loss.item()
if batch % 2 == 0:
loss, current = loss.item(), batch * len(X)
print(f"loss: {loss:>7f} [{current:>5d}/{size:>5d}]")
print(f"Train Error: \n Avg loss: {train_loss_sum/num_batches:>8f} \n")
return train_loss_sum/num_batches
def test(dataloader, model, loss_fn):
num_batches = test_examples / batch_size
test_loss = 0
with torch.no_grad():
for X, y in dataloader:
# move X, y to gpu
if torch.cuda.is_available():
X = X.to('cuda', dtype=torch.float32)
y = y.to('cuda', dtype=torch.float32)
pred = model(X)
test_loss += loss_fn(pred, y).item()
test_loss /= num_batches
print(f"Test Error: \n Avg loss: {test_loss:>8f} \n")
return test_loss
if __name__ == '__main__':
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print('Using {} device'.format(device))
num_inputs = 200
true_w = np.ones(num_inputs) * 0.01
true_b = 0.78
test_examples = 100
train_examples = 20
num_examples = test_examples + train_examples
f1, labels = synthetic_data(true_w, true_b, num_examples)
print(f1.shape)
print(labels.shape)
l1_loss_fn = torch.nn.MSELoss()
learning_rate = 0.01
model = TestNet(num_inputs)
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, weight_decay=0)
model = model.to(device)
print(model)
epochs = 100
model.train()
batch_size = 5
train_data = (torch.tensor(f1[:train_examples,]), torch.tensor(labels[:train_examples,]))
test_data = (torch.tensor(f1[train_examples:,]), torch.tensor(labels[train_examples:,]))
train_dataloader = load_array(train_data ,batch_size, True)
test_dataloader = load_array(test_data ,batch_size, True)
# verify dataloader
# for x,y in train_dataloader:
# print(x.shape)
# print(y.shape)
# print(torch.matmul( x , torch.tensor(true_w)) + torch.tensor(true_b))
# print(y)
# break
param_iter = model.parameters()
print('W = ')
print(next(param_iter).shape)
print('b = ')
print(next(param_iter).dtype)
model.train()
for t in range(epochs):
print(f"Epoch {t+1}\n-------------------------------")
train_l = train(train_dataloader, model, l1_loss_fn, optimizer, lambda_val=3)
test_l = test(test_dataloader, model, l1_loss_fn)
ydata0.append(train_l*10)
ydata1.append(test_l*10)
xdata0.append(t)
xdata1.append(t)
# print(test_l)
# print(train_l)
print("Done!")
init_and_show()
param_iter = model.parameters()
# print('W = ')
# print(next(param_iter).shape)
# print('b = ')
# print(next(param_iter).shape)
print('w的L2范数是:', np.linalg.norm(next(param_iter).to('cpu').detach().numpy()))
print('b的L2范数是:', np.linalg.norm(next(param_iter).to('cpu').detach().numpy()))
过拟合
这个时候是未添加l2到损失函数的。
train函数如下:
train_l = train(train_dataloader, model, l1_loss_fn, optimizer, lambda_val=0)
训练结果如下,存在严重的过拟合现象:
权重衰减
我们先对w进行权重衰减。
train函数如下:
train_l = train(train_dataloader, model, l1_loss_fn, optimizer, lambda_val=3)
训练结果如下,过拟合现象出现了缓解:
权重衰减适应性
我们对w和b同时进行权重衰减。train函数如下:
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, weight_decay=3)
train_l = train(train_dataloader, model, l1_loss_fn, optimizer, lambda_val=0)
训练结果如下,我们发现,过拟合现象并没有缓解:
就是上图引起了我的兴趣,因为我们引入了权重衰减,但是其并没有缓解,这是为什么呢?还记得我们权重衰减的目标是将权重的范数逼*于0吗?但是我们b是一个不接*于0的常量,因此过拟合并没有缓解。
我们对w和b同时进行权重衰减。我们修改,train函数如下,主要将true_b调整为接*于0,这样我们同时对w,b进行衰减就是合理的:
true_b = 0.01
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, weight_decay=3)
train_l = train(train_dataloader, model, l1_loss_fn, optimizer, lambda_val=0)
我们通过上面两张图的结果,说明的权重衰减的适应性以及目标。注意我们一般情况下不会对b进行权重衰减,因为其是常量。我看学*资料上并没有介绍为什么偏置不能够进行权重衰减,这里我做的这个实验可以当做一个原因吧。
pytorch的优化器里面的weight_decay参数是对所有参数进行衰减,要注意这个问题,若想单独对w进行衰减,请分别对不同的参数设定不同的优化器。这一块网上资料很多,我就不多说了。
Dropout
首先,Dropout是在([Srivastava et al., 2014] Srivastava, N., Hinton, G., Krizhevsky, A., Sutskever, I., & Salakhutdinov, R. (2014). Dropout: a simple way to prevent neural networks from overfitting. The Journal of Machine Learning Research, 15(1), 1929‒1958.)一文中引出的。
对于Dropout,我们可以从资料里面的代码里面看到其相关的原理:
def dropout_layer(X, dropout):
assert 0 <= dropout <= 1
# 在本情况中,所有元素都被丢弃。
if dropout == 1:
return np.zeros_like(X)
# 在本情况中,所有元素都被保留。
if dropout == 0:
return X
mask = np.random.uniform(0, 1, X.shape) > dropout
print(mask.astype(np.float32))
return mask.astype(np.float32) * X / (1.0 - dropout)
其工作如下:在训练的时候,按照传入的概率p丢弃一部分输出,并除以1-p。在测试的时候,跳过dropout_layer。其能正常工作的关键就是‘玄学’,打破了前一层和后一层的特定关联,破坏了两层之间的特定关联,缓解了过拟合。这个部分,建议多体会,虽然随机置0了部分值,但是输出规模是的趋势是一定的。
虽然我们从这里说明了其生效的原理,但是我们并没有解释为啥这样写是合理的?注意为何我们要在训练的时候除以1-p呢?
其实我们可以看到,Dropout是针对输出的,当我们只要保证训练和测试的输出规模保持一致,就可以保证测试和训练的结果是一致的。这里的规模保持一致,其实就是他们两个的期望保持一致。定义输入为X, dropout概率为p(以p的概率丢弃),那么\(E(x) = ((1-p)X + p*0)/(1-p) = X\)。因此,我们也可以得到结论,我们在训练时dropout生效,测试时直接跳过dropout层,这两种情况下的X的规模是一致的,不影响我们的网络结果。
此外,我们从这里可以看到,dropout是以概率来丢弃相关的输入X,那么我们必须在X规模足够大的情况下使用dropout,才能保证剩下的X能够学到足够的特征。因此,我们平常一般把dropout放在全连接层后。
下面,我们自己构造一个分类网络,使用FashionMNIST数据集(60000训练,10000测试)。然后在全连接层后面接dropout层,默认是不丢弃任何项,dropout的p=0,代码如下:
import torch
from torch import nn
import numpy as np
import matplotlib.pyplot as plt
from torch.utils import data
from matplotlib.pyplot import MultipleLocator
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
fig, ax = plt.subplots()
xdata, ydata = [[], [], [], []], [[], [], [], []]
line0, = ax.plot([], [], 'r-', label='TrainError')
line1, = ax.plot([], [], 'b-', label='TrainAcc')
line2, = ax.plot([], [], 'g-', label='TestError')
line3, = ax.plot([], [], 'y-', label='TestAcc')
def init_and_show():
ax.set_xlabel('epoch')
ax.set_ylabel('loss/acc')
ax.set_title('Train/Test Loss/Acc')
ax.set_xlim(0, epochs)
ax.set_ylim(0, 1)
# ax.set_yscale('log')
# y_locator = MultipleLocator(0.1)
# ax.yaxis.set_major_locator(y_locator)
ax.legend([line0, line1, line2, line3], ('TrainError', 'TrainAcc', "TestError", "TestAcc"))
# ax.legend([line1], ('TestError', ))
line0.set_data(xdata[0], ydata[0])
line1.set_data(xdata[1], ydata[1])
line2.set_data(xdata[2], ydata[2])
line3.set_data(xdata[3], ydata[3])
plt.show()
def dropout_layer(X, dropout):
assert 0 <= dropout <= 1
# 在本情况中,所有元素都被丢弃。
if dropout == 1:
return np.zeros_like(X)
# 在本情况中,所有元素都被保留。
if dropout == 0:
return X
mask = np.random.uniform(0, 1, X.shape) > dropout
print(mask.astype(np.float32))
return mask.astype(np.float32) * X / (1.0 - dropout)
class TestNet(nn.Module):
def __init__(self, dropout_p_arr = [0, 0]):
super(TestNet, self).__init__()
self.test_net = nn.Sequential(
torch.nn.Linear(1*28*28, 512),
torch.nn.ReLU(),
torch.nn.Dropout(dropout_p_arr[0]),
torch.nn.Linear(512, 512),
torch.nn.ReLU(),
torch.nn.Dropout(dropout_p_arr[1]),
torch.nn.Linear(512, 10),
)
def forward(self, x):
# print(x.dtype)
#
return self.test_net(x)
def LoadFashionMNISTByTorchApi():
resize=28
trans = [transforms.ToTensor()]
if resize:
trans.insert(0, transforms.Resize(resize))
trans = transforms.Compose(trans)
# 60000*28*28
training_data = datasets.FashionMNIST(
root="..\data",
train=True,
download=True,
transform=trans
)
# 10000*28*28
test_data = datasets.FashionMNIST(
root="..\data",
train=False,
download=True,
transform=trans
)
# labels_map = {
# 0: "T-Shirt",
# 1: "Trouser",
# 2: "Pullover",
# 3: "Dress",
# 4: "Coat",
# 5: "Sandal",
# 6: "Shirt",
# 7: "Sneaker",
# 8: "Bag",
# 9: "Ankle Boot",
# }
# figure = plt.figure(figsize=(8, 8))
# cols, rows = 3, 3
# for i in range(1, cols * rows + 1):
# sample_idx = torch.randint(len(training_data), size=(1,)).item()
# img, label = training_data[sample_idx]
# figure.add_subplot(rows, cols, i)
# plt.title(labels_map[label])
# plt.axis("off")
# plt.imshow(img.squeeze(), cmap="gray")
# plt.show()
return training_data, test_data
def train(dataloader, model, loss_fn, optimizer):
num_batches = len(dataloader)
size = num_batches*batch_size
train_loss_sum = 0
train_acc_sum = 0
for batch, (X, y) in enumerate(dataloader):
# move X, y to gpu
if torch.cuda.is_available():
X = X.to('cuda', dtype=torch.float32)
y = y.to('cuda')
# Compute prediction and loss
pred = model(X.reshape(batch_size, -1))
# print(pred.shape)
# print(y.shape)
loss = loss_fn(pred, y)
# Backpropagation
optimizer.zero_grad()
loss.backward()
optimizer.step()
train_loss_sum += loss.item()
# cal train acc
pred = model(X.reshape(batch_size, -1))
train_acc_sum += (pred.argmax(1) == y).type(torch.float).sum().item()/batch_size
if batch % 100 == 0:
loss, current = loss.item(), batch * len(X)
print(f"loss: {loss:>7f} [{current:>5d}/{size:>5d}]")
print(f"Train Error: \n Avg loss: {train_loss_sum/num_batches:>8f} \n")
print(f"Train Acc : \n Avg acc : {train_acc_sum/num_batches:>8f} \n")
return train_loss_sum/num_batches, train_acc_sum/num_batches
def test(dataloader, model, loss_fn):
num_batches = len(dataloader)
test_loss = 0
test_acc = 0
with torch.no_grad():
for X, y in dataloader:
# move X, y to gpu
if torch.cuda.is_available():
X = X.to('cuda', dtype=torch.float32)
y = y.to('cuda')
pred = model(X.reshape(batch_size, -1))
test_loss += loss_fn(pred, y).item()
test_acc += (pred.argmax(1) == y).type(torch.float).sum().item()/batch_size
test_loss /= num_batches
test_acc /= num_batches
print(f"Test Error: \n Avg loss: {test_loss:>8f} \n")
print(f"Test Acc : \n Avg loss: {test_acc:>8f} \n")
return test_loss, test_acc
if __name__ == '__main__':
device = 'cuda' if torch.cuda.is_available() else 'cpu'
print('Using {} device'.format(device))
loss_fn = torch.nn.CrossEntropyLoss()
learning_rate = 0.5
# [0.4, 0.7]
model = TestNet()
optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate, weight_decay=0)
model = model.to(device)
print(model)
epochs = 10
model.train()
batch_size = 200
train_data, test_data = LoadFashionMNISTByTorchApi()
train_dataloader = DataLoader(train_data, batch_size, shuffle=True)
test_dataloader = DataLoader(test_data, batch_size, shuffle=True)
print(len(train_dataloader))
print(len(test_dataloader))
# #verify dataloader
# for x,y in train_dataloader:
# print(x.shape)
# print(y.shape)
# break
param_iter = model.parameters()
print(next(param_iter).shape)
for t in range(epochs):
print(f"Epoch {t+1}\n-------------------------------")
model.train()
train_l, train_acc = train(train_dataloader, model, loss_fn, optimizer)
model.eval()
test_l, test_acc = test(test_dataloader, model, loss_fn)
xdata[0].append(t)
xdata[1].append(t)
xdata[2].append(t)
xdata[3].append(t)
ydata[0].append(train_l)
ydata[1].append(train_acc)
ydata[2].append(test_l)
ydata[3].append(test_acc)
print("Done!")
init_and_show()
正常过拟合
直接用上面代码进行训练后得到结果如下:
我们可以发现,测试准确率比训练准确率低,满足过拟合的现象。
启用dropout
修改训练代码:
# [0.4, 0.7]
model = TestNet([0.4, 0.7])
训练结果如图:
我们可以很直观的发现,训练和测试的acc和error出现了重合的情况,至少证明了过拟合现象出现了缓解。
后记
对于权重衰减,一般就是要将参数的L2范数尽量学*来趋*于0,这样模型复杂度变小。此外权重衰减还可以将参数限制到一个稳定的范围,避免出现了较大的波动。对于稳定的学*过程是有帮助的。
对于Dropout来说,就是打破一些输出比较大的相关层的关联性,注意,其是针对输出,并不是针对权重。有些时候,相关层的关联性就是我们要学的,但是有些时候,这种关联性可能就是不需要的,所以通过这种‘玄学’的方式,在训练的时候,以概率性来丢弃某些输出,打破这项输出和下一层的关联性。这对于大的网络来说,是有意义的。
参考文献
- https://github.com/d2l-ai/d2l-zh/releases (V1.0.0)
- https://github.com/d2l-ai/d2l-zh/releases (V2.0.0 alpha1)
- Srivastava, N., Hinton, G., Krizhevsky, A., Sutskever, I., & Salakhutdinov, R. (2014). Dropout: a simple way to prevent neural networks from overfitting. The Journal of Machine Learning Research, 15(1), 1929‒1958.
打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)
PS: 请尊重原创,不喜勿喷。
PS: 要转载请注明出处,本人版权所有。
PS: 有问题请留言,看到后我会第一时间回复。
DL基础补全计划(四)---对抗过拟合:权重衰减、Dropout的更多相关文章
- DL基础补全计划(二)---Softmax回归及示例(Pytorch,交叉熵损失)
PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明 本文作为本人csdn blog的主站的备份.(Bl ...
- DL基础补全计划(三)---模型选择、欠拟合、过拟合
PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明 本文作为本人csdn blog的主站的备份.(Bl ...
- DL基础补全计划(六)---卷积和池化
PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明 本文作为本人csdn blog的主站的备份.(Bl ...
- DL基础补全计划(一)---线性回归及示例(Pytorch,平方损失)
PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明 本文作为本人csdn blog的主站的备份.(Bl ...
- DL基础补全计划(五)---数值稳定性及参数初始化(梯度消失、梯度爆炸)
PS:要转载请注明出处,本人版权所有. PS: 这个只是基于<我自己>的理解, 如果和你的原则及想法相冲突,请谅解,勿喷. 前置说明 本文作为本人csdn blog的主站的备份.(Bl ...
- OSPF补全计划-0 preface
哇靠,一看日历吓了我一跳,我这一个月都没写任何东西,好吧,事情的确多了点儿,同事离职,我需要处理很多untechnical的东西,弄得我很烦,中间学的一点小东西(关于Linux的)也没往这里记,但是我 ...
- 【hjmmm网络流24题补全计划】
本文食用方式 按ABC--分层叙述思路 可以看完一步有思路后自行思考 飞行员配对问题 题目链接 这可能是24题里最水的一道吧... 很显然分成两个集合 左外籍飞行员 右皇家飞行员 跑二分图最大匹配 输 ...
- 2018.我的NOIP补全计划
code: efzoi.tk @ shleodai noip2011 D1 选择客栈 这道题是一道大水题,冷静分析一会就会发现我们需要维护最后一个不合法点和前缀和. 维护最后一个不合法点只要边扫描边维 ...
- OSPF补全计划-2
想起来几个面试题: 1. OSPF在什么情况下会stuck in Exstart /Exchange状态? 我知道的一个答案是两个端口的mtu不一致.当然整个也不是绝对,因为可以用ip ospf mt ...
- OSPF补全计划-1
OSPF全称是啥我就不絮叨了,什么迪杰斯特拉,什么开放最短路径优先算法都是人尽皆知的事儿,尤其是一提算法还会被学数据结构的童鞋鄙视,干脆就不提了,直接开整怎么用吧.(不过好像真有人不知道OSPF里的F ...
随机推荐
- 201871010111-刘佳华 实验二 个人项目—《D[01]背包问题》项目报告
实验二 软件工程个人项目 ========== 时间:2021-3-18 项目 内容 课程班级博客链接 课程链接 这个作业要求链接 作业要求 我的课程学习目标 1.了解软件工程过程中个人项目的开发流程 ...
- 如何用低代码实现批量导出PDF?
前言 事情是这样的,熟悉我们的朋友都知道,我司有一个为广大开发者朋友们提供学习帮助的地方,叫做新手训练营,具体的内容就是会针对初次接触葡萄城产品和技术的用户,通过 2-3 天的集中学习,采用直播授课的 ...
- NC16664 [NOIP2004]合唱队形
题目链接 题目 题目描述 N位同学站成一排,音乐老师要请其中的(N-K)位同学出列,使得剩下的K位同学排成合唱队形. 合唱队形是指这样的一种队形:设K位同学从左到右依次编号为1,2-,K,他们的身高分 ...
- Centos8 安装 MySQL8.0.26
下载 访问 https://dev.mysql.com/downloads/mysql/ 选择 Red Hat Enterprise Linux / Oracle Linux 选择 Red Hat E ...
- Vuex和普通全局对象
Vuex中的核心方法 Vuex是一个专为Vue.js应用程序开发的状态管理模式,其采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化.每一个Vuex应用的核心就是 ...
- P3374 【模板】树状数组 1(线段树)
[模板]树状数组 1 题目描述 如题,已知一个数列,你需要进行下面两种操作: 将某一个数加上 x 求出某区间每一个数的和 输入格式 第一行包含两个正整数 n,m ,分别表示该数列数字的个数和操作的总个 ...
- SQLite 入门教程
不是 MySQL 用不起,而是 SQLite 更有性价比,绝大多数的 Web 应用 SQLite 都可以满足. SQLite 是一个用 C 语言编写的开源.轻量级.快速.独立且高可靠性的 SQL 数据 ...
- centos上使用makefile编译sliver时 提示gcc 错误,cannot find -ldl cannot find -lpthread cannot find -lc
github.com/bishopfox/sliver/server /usr/local/go/pkg/tool/linux_amd64/link: running gcc failed: exit ...
- chrome浏览器配置自定义搜索引擎
chrome谷歌浏览器配置自定义搜索引擎 放弃百度搜索已经酝酿许久,现在搜索结果简直不忍直视.如果你想放弃使用百度搜索,并转向其他搜索引擎,头条搜索可能是一个不错的选择. 使用以下方式可以丝滑的使用其 ...
- 【算法day2】复杂度和简单排序算法(2)
插入排序 有以下数组 数组:[2,4,3,6,1] 序号:[0,1,2,3,4] 第一次排序(范围0~0):2左边没东西,不动 第二次排序(范围0~1):4左边是2,4大不动 第三次排序(范围0~2) ...