PyTorch使用经验整理

1.train_test_split()函数的应用心得

X_train, X_last, Y_train, Y_last = train_test_split(X, Y, test_size=0.1, shuffle=False, random_state=666)

只有当shuffle=True时,random_state才起作用或者说才有意义,因为“random_state : Controls the shuffling applied to the data before applying the split.”即,如果shuffle=False了的话,就不会洗牌了,那么random_state这么一个控制怎么洗牌的参数也就没用了。

2.LSTM参数构成,输入输出的理解

关于输入输出的理解:深入理解PyTorch中LSTM的输入和输出(从input输入到Linear输出)_lstm的输出-CSDN博客

# input_size: 输入数据最后一维的大小,比如输入一个句子,句子中每个单词都是5个字母, 则input_size = 5.
# hidden_size: 一个LSTM内部中神经元的个数.
# num_layers: LSTM的层数.
# bias: 每个神经元是否具有偏置.
# batch_first: 该参数是影响输入数据的格式问题,若batch_first = True, 则输入数据的格式应为:(Batch_size, Length, input_dim),否则为(Length, Batch_size, input_dim).
# dropout: LSTM模块内神经元的舍弃率.
# bidirectional: 是否是双向的LSTM网络.
self.model_name = 'TextRNN'
self.train_path = dataset + '/data/train.txt'                                # 训练集
self.dev_path = dataset + '/data/dev.txt'                                    # 验证集
self.test_path = dataset + '/data/test.txt'                                  # 测试集
self.class_list = [x.strip() for x in open(dataset + '/data/class.txt').readlines()] # 类别名单
self.vocab_path = dataset + '/data/vocab.pkl'                                # 词表
self.save_path = dataset + '/saved_dict/' + self.model_name + '.ckpt'        # 模型训练结果
self.log_path = dataset + '/log/' + self.model_name
self.embedding_pretrained = torch.tensor(
    np.load(dataset + '/data/' + embedding)["embeddings"].astype('float32'))\
    if embedding != 'random' else None                                       # 预训练词向量
self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')   # 设备
    
self.dropout = 0.5  # 随机失活
self.require_improvement = 1000  # 若超过1000batch效果还没提升,则提前结束训练
self.num_classes = len(self.class_list)                         # 类别数
self.n_vocab = 0  # 词表大小,在运行时赋值
self.num_epochs = 30  # epoch数
self.input_size = 85  # 输入数据维度 同self.embed
self.num_output = 1  # 输出数据维度 同num_class
self.batch_size = 128  # mini-batch大小(dataloader里也有一个batchsize,需要注意)
self.pad_size = 32  # 每句话处理成的长度(短填长切)
self.learning_rate = 1e-3  # 学习率
self.embed = self.embedding_pretrained.size(1)\
   if self.embedding_pretrained is not None else 300        # 字向量维度, 若使用了预训练词向量,则维度统一
self.hidden_size = 128  # lstm隐藏层
self.num_layers = 2  # lstm层数

Output参数其实和输入数据格式一致(为[batch, length, other]),使用Numpy数组切[ :, -1, :]方法,可以获取最后一项的预测值。(注意,此方法可以进一步改成多步长预测

关于数组切片:Python中numpy数组切片:print(a[0::2])、[::-1]、[::2]、[:,2]、[1:,-1:]、[ : ,: -1],[:,-2:]、[ : n]、[m : ]等含义(详细)_锵锵锵锵~蒋-华为云开发者联盟

3.关于lstm()中的batch_first

self.lstm = nn.LSTM(config.input_size, config.hidden_size, config.num_layers, batch_first=True)

该参数是影响输入数据的格式问题,若batch_first = True, 则输入数据的格式应为:(Batch_size, Length, input_dim),否则为(Length, Batch_size, input_dim)。

4.关于PyTorch中的device

在PyTorch中,如果需要使用GPU进行训练,那么需要将Torch张量以及模型都载入到GPU中。

# 指定运行设备 (两种写法)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# 或者直接使用
device = "cuda:0" if torch.cuda.is_available() else "cpu"
# 转换为tensor,并且需要把数据上传至GPU(这里给出直接指定和.cuda(),以及.to(device)三种方法)
# 如果是自定义tensor数据,如下:
X = torch.rand(1, 28, 28, device=device) #1
inputs = torch.from_numpy(x_train).cuda() #2
inputs = torch.from_numpy(x_train).to(device) #3
#对于模型
# 将模型载入GPU
model.to(device)
print(next(model.parameters()).device) #检查设备

同样的,在输出模型数据时,也要将数据输出至CPU,以便后续处理以及构图。

y = model(x).data.cpu().numpy()

5.关于PyTorch训练模型与校验模型

model.train()与model.eval()

训练完train_datasets之后,model要来测试样本了。在model(test_datasets)之前,需要加上model.eval(). 否则的话,有输入数据,即使不训练,它也会改变权值。这是model中含有batch normalization层所带来的的性质。

训练完train_datasets之后,model要来测试样本了。在model(test_datasets)之前,需要加上model.eval(). 否则的话,有输入数据,即使不训练,它也会改变权值。这是model中含有batch normalization层所带来的的性质。

注意:with torch.no_grad()

无论是train() 还是eval() 模式,各层的gradient计算和存储都在进行且完全一致,只是在eval模式下不会进行反向传播。

.eval模式不会影响各层的gradient计算行为,即gradient计算和存储与training模式一样,只是不进行反向传播(back probagation)。

而with torch.no_grad()则主要是用于停止autograd模块的工作,以起到加速和节省显存的作用。它的作用是将该with语句包裹起来的部分停止梯度的更新,从而节省了GPU算力和显存,但是并不会影响dropout和BN层的行为。若想节约算力,可在test阶段带上torch.no_grad(),示例代码:

def test(model,dataloader):
    model.eval()  # 切换到测试模式
    with torch.no_grad():  #with下内容不进行grad计算
        ...

可以参考:【PyTorch】搞定网络训练中的model.train()和model.eval()模式 - 知乎加深理解。

6.使用PCA时对于训练集和测试集的处理

(但有一个问题,模型搭建之后若独立运用,怎么输入?)

使用sklearn的PCA包,涵盖训练集与测试集的处理:使用PCA加速机器学习训练和预测 - 简书 也可参考:机器学习(四)——PCA主成分分析_pca.explained_variance_ratio_-CSDN博客

使用效果:LSTM转换为窗口数据,速度快了100倍?

from sklearn.decomposition import PCA
pca = PCA(40)
pca.fit(data_x)
x_data_reduction = pca.transform(data_x) # 得到降维后的数据
​
init_data = inverse_transform(x_data_reduction) # 将降维后的数据转换成原始数据
fit_transform(X) # 用X来训练PCA模型,同时返回降维后的数据
# 此外,还有get_covariance()、get_precision()、get_params(deep=True)、score(X, y=None)等方法,以后用到再补充吧。

不使用PCA包,自己进行PCA的矩阵运算:机器学习笔记-10-主成分分析 - 知乎

7.TensorBoard使用

Before going further, more details on TensorBoard can be found at https://www.tensorflow.org/tensorboard/

from torch.utils.tensorboard import SummaryWriter
​
writer = SummaryWriter()
​
writer.add_image('images', grid, 0)
writer.add_graph(model, images)
writer.add_scalar('Loss/train', np.random.random(), n_iter)
writer.add_scalar('Loss/test', np.random.random(), n_iter)
writer.add_scalar('Accuracy/train', np.random.random(), n_iter)
writer.add_scalar('Accuracy/test', np.random.random(), n_iter)
​
writer.close()

使用的时候,从控制台输入

// 首先需要安装
pip install tensorboard
// 启动TensorBoard
tensorboard --logdir=runs

8.**Checkpoint预训练与保存最佳模型

#第一个是保存模型
def save_checkpoint (state,file_name):
    print('saving check_point')
    torch.save(state,file_name)
#第二个是加载模型
def load_checkpoint(checkpoint):
    print('Load _model')
    model.load_state_dict(checkpoint['state_dict'])
    optimizer.load_state_dict(checkpoint['optimizer'])
#我这里只演示了加载模型和优化器的checkpoint
​
#然后我们需要在训练模型的过程中把checkpoint保存下来
for epoch in num_epoch:
#这里可以设置每隔多少个epoch保存一次
#比如每隔2个epoch保存一次
    if epoch% 2 == 0:
        checkpoint = {'state_dict': model.state_dict(), 'optimizer': optimizer.state_dict()}
        save_checkpoint(checkpoint,'cnn_model.pth')#这里后面这个是你想要设置的checkpoint文件的名字,ubuntu上默认保存为zip文件
#这里保存的是相对路径
#在保存完之后就可以读取了
​
#注意我们读取的时候要在训练过程的前面,模型和优化器定义的后面
pre_train = True
if pre_train:
    load_checkpoint(torch.load('cnn_model.pth'))
#这样模型就会加载训练过的参数了,也可以用这种方式加载与训练模型

参考:

pytorch模型的保存和加载、checkpoint_pytorch保存checkpoint模型-CSDN博客

pytorch保存和加载预模型和checkpoint - 知乎

9.理解loss.backward()与optimizer.step() *

loss.backward()故名思义,就是将损失loss 向输入侧进行反向传播,同时对于需要进行梯度计算的所有变量x (requires_grad=True),计算梯度,并将其累积到梯度 中备用,即:

optimizer.step()是优化器对 x 的值进行更新,以随机梯度下降SGD为例:学习率(learning rate, lr)来控制步幅,即:_,减号是由于要沿着梯度的反方向调整变量值以减少Cost。

10.loss.item()与一个epoch下的平均loss

.item()方法是,取一个元素张量里面的具体元素值并返回该值,可以将一个零维张量转换成int型或者float型,在计算loss,accuracy时常用到。

作用: 1.item()取出张量具体位置的元素元素值 2.并且返回的是该位置元素值的高精度值 3.保持原元素类型不变;必须指定位置 4.节省内存(不会计入计算图)***

参考: loss.item()用法和注意事项详解-CSDN博客

对于一个epoch下的平均loss:

for step, (b_x, b_y) in enumerate(train_loader):
    output = model(b_x)
    loss = loss_func(output, b_y)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    # “+= loss.item()*size” 表示这一步下所有的loss
    train_loss += loss.item() * b_x.size(0)
    train_num += b_x.size(0)
    if total_batch % 1000 == 0:
        true = b_y.data.cpu().numpy()
        predict = model(b_x)[:, 0].data.cpu().numpy()
        train_rmse = math.sqrt(mean_squared_error(true, predict))
        r2 = r2_score(true, predict)
        time_dif = get_time_dif(start_time)
        val_loss = evaluate(model, config)
    total_batch += 1
# 最后append汇总的时候,该epoch下的平均loss
train_loss_all.append(train_loss / train_num)
   
# 另一种写法
    for epoch in range(n_epochs):
        for step, batch in enumerate(trnloader):
            tr_loss += loss.item()
        epoch_loss = tr_loss / len(trnloader)

11.调用完评估模块(使用了model.eval())后,要记得添加model.train()

调用完评估模块(使用了model.eval())后,要记得添加model.train()

调用完评估模块(使用了model.eval())后,要记得添加model.train()

调用完评估模块(使用了model.eval())后,要记得添加model.train()

12.model.zero_grad() 与optimizer.zero_grad()

当使用optimizer = optim.Optimizer(net.parameters())设置优化器时,此时优化器中的param_groups等于模型中的parameters(),此时,二者是等效的,从二者的源码中也可以看出来。

当多个模型使用同一个优化器时,二者是不同的,此时需要根据实际情况选择梯度的清除方式。 当一个模型使用多个优化器时,二者是不同的,此时需要根据实际情况选择梯度的清除方式。

原文链接:PyTorch中的model.zero_grad() 与 optimizer.zero_grad()-CSDN博客

13.**模型保存读取详解(预训练相关)和checkpoint一起

Pytorch中模型的保存与迁移: Pytorch中模型的保存与迁移

注意,如果需要训练模型的权重,并且继续训练,优化器的参数必须也要保存,代码如下:

# 得到最好那次的模型
            if phase == 'valid' and epoch_acc > best_acc:
                best_acc = epoch_acc
                best_model_wts = copy.deepcopy(model.state_dict())
                state = {
                  'state_dict': model.state_dict(),
                  'best_acc': best_acc,
                  'optimizer' : optimizer.state_dict(),
                }
                torch.save(state, filename)
            if phase == 'valid':
                val_acc_history.append(epoch_acc)
                valid_losses.append(epoch_loss)
                scheduler.step(epoch_loss)
            if phase == 'train':
                train_acc_history.append(epoch_acc)
                train_losses.append(epoch_loss)

继续训练:

# Load the checkpoint

checkpoint = torch.load(filename)
best_acc = checkpoint['best_acc']
model_ft.load_state_dict(checkpoint['state_dict'])
optimizer.load_state_dict(checkpoint['optimizer'])
#model_ft.class_to_idx = checkpoint['mapping']
加载模型,进行测试结果:

model_ft, input_size = initialize_model(model_name, 102, feature_extract, use_pretrained=True)

# GPU模式
model_ft = model_ft.to(device)

# 保存文件的名字
filename='seriouscheckpoint.pth'

# 加载模型
checkpoint = torch.load(filename)
best_acc = checkpoint['best_acc']
model_ft.load_state_dict(checkpoint['state_dict'])

14.LSTM与LSTMcell(包括前项传播与模型搭建的学习)

# LSTM example:
>>> rnn = nn.LSTM(10, 20, 2)
>>> input = torch.randn(5, 3, 10)
>>> h0 = torch.randn(2, 3, 20)
>>> c0 = torch.randn(2, 3, 20)
>>> output, (hn, cn) = rnn(input, (h0, c0))
# LSTMCell example:
>>> rnn = nn.LSTMCell(10, 20)
>>> input = torch.randn(3, 10)
>>> hx = torch.randn(3, 20)
>>> cx = torch.randn(3, 20)
>>> output = []
>>> for i in range(6):
        hx, cx = rnn(input[i], (hx, cx))
        output.append(hx)

主要的区别在于:

  1. LSTM: the argument 2, stands num_layers, number of recurrent layers. There are seq_len * num_layers=5 * 2 cells. No loop but more cells.

  2. LSTMCell: in for loop (seq_len=5 times), each output of ith instance will be input of (i+1)th instance. There is only one cell, Truly Recurrent

If we set num_layers=1 in LSTM or add one more LSTMCell, the codes above will be the same.Obviously, It is easier to apply parallel computing in LSTM.

Cells的使用如下

def __init__(self, config):
    super(LSTM, self).__init__()
    self.input_size = config.input_size
    self.hidden_size = config.hidden_size
    self.num_directions = 1
    self.num_layers = config.num_layers

    # self.conv = nn.Sequential(
    #     nn.Conv1d(in_channels=self.in_channels, out_channels=self.out_channels, kernel_size=2),
    #     # shape(7,--)  ->(64,3,2)
    #     nn.ReLU())
    self.lstm1 = nn.LSTMCell(input_size=40, hidden_size=128)
    self.lstm2 = nn.LSTMCell(input_size=128, hidden_size=64)
    self.predict = nn.Linear(in_features=64, out_features=1)

    def forward(self, input_seq):
        # (batch_size, time_step, input_size==>(256, 24, 40)
        batch_size, seq_len = input_seq.shape[0], input_seq.shape[1]
        
        ''' 
        lstm写法
        # h0和c0也可以不指定,默认值即全为0
        out, _ = self.lstm(input_seq, (h_0, c_0))
        # 这里还有个输出_是代表不重要的意思,python程序员约定俗成的一种写法。也就是hn与cn最后lstm层的
        hiddenstate 与Cellstate
        # out, _ = self.lstm(input_seq)
        # (batch_size, time_step, hidden_size)
        # (N, L, D * hidden_size)N= batch size, L= sequence length,  D= 2 if bidirectional=True 
        otherwise 1
        # out = self.predict(out)
        '''

        # cells写法
        h_0 = torch.randn(batch_size, 128).to(device)
        c_0 = torch.randn(batch_size, 128).to(device)
        h_1 = torch.randn(batch_size, 64).to(device)
        c_1 = torch.randn(batch_size, 64).to(device)
        out = []
        for i in range(input_seq.size()[1]):
            h_0, c_0 = self.lstm1(input_seq[:, i, :], (h_0, c_0))
            h_1, c_1 = self.lstm2(h_0, (h_1, c_1))
            out.append(h_1)
            out = torch.stack(out, dim=0)
            # torch.Size([24, batch_size, 64])
            out = self.predict(out)
            # torch.Size([24, 32, 1])
            # return out[:, -1, :]
            return out[-1, :, :]

15.*optimizer学习率衰减的函数扩展

optim api:torch.optim — PyTorch 2.1 documentation

torch.optim.lr_scheduler.StepLR(*optimizer*, *step_size*, *gamma=0.1*, *last_epoch=- 1*, *verbose=False*)

学习率按照epoch数量衰减。

16.nn.Sequential(*layers)

在Python中: 作用在形参上,代表这个位置接收任意多个非关键字参数,转化成元组方式; 作用在实参上,代表的是将输入迭代器拆成一个个元素。

从nn.Sequential的定义来看,输入要么是orderdict,要么是一系列的模型,遇到list,必须用*号进行转化,否则会报错 TypeError: list is not a Module subclass

17.权重初始化种子(!!!重中之重!!!)

seed在深度学习代码中叫随机种子,设置seed的目的是由于深度学习网络模型中初始的权值参数通常都是初始化成随机数,而使用梯度下降法最终得到的局部最优解对于初始位置点的选择很敏感,设置了seed就相当于规定了初始的随机值。

即产生随机种子意味着每次运行实验,产生的随机数都是相同的

为将模型在初始化过程中所用到的“随机数”全部固定下来,以保证每次重新训练模型需要初始化模型参数的时候能够得到相同的初始化参数,从而达到稳定复现训练结果的目的。【血泪教训】

  1. torch.manual_seed(seed):设置生成随机数的种子【You can use torch.manual_seed() to seed the RNG for all devices (both CPU and CUDA)】

  2. torch.cuda.manual_seed_all():为所有的GPU设置种子【使用多个GPU】

  3. torch.cuda.manual_seed(seed):在GPU中设置生成随机数的种子【当设置的种子固定下来的时候,之后依次pytorch生成的随机数序列也被固定下来,当只调用torch.cuda.manual_seed()一次时并不能生成相同的随机数序列,要得到相同的随机数序列就需要每次产生随机数的时候都要调用torch.cuda.manual_seed()】

  4. dgl.random.seed(seed):在DGL中设置随机方法的种子

  5. random.seed(seed):改变随机生成器的种子,传入的数值用于指定随机数生成时所用算法开始时所选定的整数值,如果使用相同的seed()值,则每次生成的随机数都相同;如果不设置值,每次生成的随机数会不同

     random_seed = 1388
       random.seed(random_seed)
       np.random.seed(random_seed)
       torch.manual_seed(random_seed)
       torch.cuda.manual_seed_all(random_seed)
       #cudnn.benchmark = True       
       torch.backends.cudnn.deterministic = True
       torch.backends.cudnn.benchmark = False

参考:深度学习中的随机初始化种子random.seed()与参数初始化_random.seed(seed)-CSDN博客

18.初始化方法

1.随机初始化

将参数设置为接近0的很小的随机数(有正有负),在实际中,随机参数服从高斯分布/正态分布(Gaussian distribution / normal distribution)和均匀分布(uniform distribution)都是有效的初始化方法。

正态分布初始化:又称高斯分布初始化,参数从一个固定均值和固定方差的高斯分布进行随机初始化 均匀分布初始化:在一个给定区间 [ -r,r ] 内采用均匀分布来初始化参数

2.标准初始化

标准初始化方法更适用于 tanh激活函数 正态分布:标准正态初始化方法通过对方差乘以一个系数确保每层神经元的输出具有相同的方差,提高训练收敛速度 均匀分布:标准均匀初始化方法保证了激活函数的输入值的均值为0,方差为常量三分之一,和网络的层数和神经元的数量无关

3.Xavier初始化和He初始化

Xavier初始化适用于Losgistictanh激活函数 He初始化更适用于ReLu激活函数

4.迁移学习初始化

预训练模型的参数 作为新任务上的初始化参数

5. 数据敏感初始化

根据自身任务数据集而特别定制的参数初始化方法


初始化实例:Reference:torch.nn.init — PyTorch 2.0 documentation

Xavier初始化 参考:深度学习中的随机初始化种子random.seed()与参数初始化_random.seed(seed)-CSDN博客

torch.nn.init.xavier_uniform_(tensor, gain=1.0)
# Examples
>>> w = torch.empty(3, 5)
>>> nn.init.xavier_uniform_(w, gain=nn.init.calculate_gain('relu'))
 
torch.nn.init.xavier_normal_(tensor, gain=1.0)
# Examples
>>> w = torch.empty(3, 5)
>>> nn.init.xavier_normal_(w)
  1. Sigmoid、Tanh激活函数时,优先考虑Xavier初始化

  2. Relu激活函数时,优先考虑He初始化方法

  3. Batch Normalization的引入,使参数初始化方法对结果的影响减弱,实际应用中可以尝试不同的初始化方法

  • 25
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值