[ML] Pytorch自学实战项目其2:一个基于rosbag信息处理,使用神经网络RNN学习的方法估算车辆yawrate,不断优化和找bug,欢迎留言一起学习

写在前面

本文是上一个项目神经网络估算车辆yawrate的后续,将架构中加入了RNN,我认为,t+1时刻的车辆状态,不仅取决于t0时刻,也和t-1 t-2 t-3 etc. 有关。
本版基于之前的设计结构,调整了数据dataset建立的方式,模型类,以及训练和测试函数。加入了部分新的细节。

在学习使用rnn过程中的疑问和部分资源见:

我将结合代码做一些自己认为重要解释,以便年纪大了记不住,也作为日后自己使用rnn的复习和参考,主要有几点:

  • 从普通全连接网络到rnn后每一步处理后的变量的维度很重要,否则结果会和预期不同,经过重要的处理后最后打印检查维度
  • 隐藏状态的传递
  • 部分处理拼接数据过程中常见的函数

12月13更新

当前效果总结推理模型效果以及两个大问题修改:BN换LN;特征选取不当

12 月6日更新

detach hidden state 的地方有点问题 重新作了修改如下:

如此结合我的main函数,在每一个epoch之间我的hidden是重新初始化的,在每一个batch之间我的hidden的反向梯度累积会detach掉。(通过GPT确认,但是也说pytorch会自动处理hidden的传播与阻断,不需要我显式的使用detach,有待大佬们留言指教)

def train_RNN(dataloader,model,loss_fn,optimizer,device):
    #hidden_prev init
    size = len(dataloader.dataset)
    hidden_prev = None
    # Set the model to training mode - important for batch normalization and dropout layers
    model.train()
    for batch, (X, y) in enumerate(dataloader):
        # print(X.shape, y.shape) 16 5 9 ;16 1 7
        # print (batch)
        X = X.to(device)  # 将数据移到设备上
        y = y.to(device)
        batch_size = X.size(0)
        hidden_prev = torch.zeros(1, batch_size, 32).to(device) if hidden_prev is None else hidden_prev
        # Detach hidden_prev from the computational graph
        hidden_prev = hidden_prev.detach() if hidden_prev is not None else None

        output, _ = model(X, hidden_prev)
        # hidden_prev = hidden_prev.detach()
        loss = loss_fn(output, y)
        model.zero_grad()
        loss.backward()
        optimizer.step()

        if batch % 50 == 0:# set print frequenz
            loss, current = loss.item(), (batch + 1) * len(X)
            print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")
            #l.append(loss.item())

GPT:
在 PyTorch 中,对于 RNN 模型,隐藏状态在每个 batch 之间的传播是由 PyTorch 自动处理的。默认情况下,PyTorch 会在每个 batch 的前向传播过程中自动处理隐藏状态,确保梯度的传播和截断。这意味着在大多数情况下,你可能不需要手动使用 detach() 来处理隐藏状态。

在你的代码中,因为你在每个循环迭代中都重新初始化了 hidden_prev,并且没有保留计算图的依赖,所以你不显式使用 detach() 也是合适的。PyTorch 会在每次前向传播时自动处理隐藏状态,不需要手动介入。

总体而言,如果你的模型结构和训练过程没有特殊的需求,PyTorch 的默认机制通常足够处理隐藏状态的传播和梯度问题。只有在特殊情况下,例如需要手动管理隐藏状态以处理长期依赖关系或梯度问题时,才需要考虑手动使用 detach()。

10月27日更新(顺序排在文末):

  • 排查昨天遇到的loss不下降过程笔记发现维度Bugs,和几处调整
  • 加入推理模型
  • 修改batch_size 为动态可变,配适隐藏状态初始化维度(其实按照排查问题后决定batch间不再传递隐藏参数,这一步也可以取消了)
  • 讨论了一下 dataloader 开不开随机的作用

预计下次更新 (更新在了另一篇文章里:实战其3里)

  • 请教了一位同事大佬,基本可行,下次研究:
    输入数据要特征处理标准化一下,
    hyperopt 调整超参数,
    模型复杂度向上升一下,
    earlystop flag,
    部署使用tensorRT
  • 超参数(作为一个大的话题学习讨论更新)
    • 学到了一手看到模型参数变化的方法

创建数据集合

input数据我依然是继承了Dataset类并使用DataLoader类完成输入数据集合的创建,事实上见我收集的rnn学习链接里的例子,如果数据不是很复杂,之间构建一个符合网络输入维度要求的tensor也是一个很好的方法。

我创建数据集合的代码是:

class CustomDataset_RNN(Dataset):
    def __init__(self, file_path):
        self.samples = [] 
        with open(file_path, 'r') as file:
            lines = file.readlines()
        data = {}
        for line in lines:
            key, value = line.split(': ')
            data[key] = float(value)
            if (key=="msg.nohCtrlOutput.targetAcceleration"):
                self.samples.append(data.copy())
                data.clear()
    def __len__(self):
        return len(self.samples)-6# -6 : -1 is special for my case and -5 is sequence length
    def __getitem__(self, idx):
        sequence = self.samples[idx:idx+5] # e.g. 0 1 2 3 4 shape is (5,9)(squence length:5,features:9)
        features=[]
        # for i in range (5):
        #     sample=sequence[i]
        for sample in sequence:
            feature = torch.tensor([
            sample.get('msg.egoEgoStatus.yawRate', 0.0),  # set default value, in some case the dataset could be not printed correctly
            sample.get('msg.egoEgoStatus.linearSpeed', 0.0),
            sample.get('msg.egoEgoStatus.accerationX', 0.0),
            sample.get('msg.egoEgoStatus.accerationY', 0.0),
            sample.get('msg.egoEgoStatus.steerWheelAngle', 0.0),
            sample.get('msg.egoEgoStatus.steerWheelAngleRate', 0.0),
            sample.get('msg.egoEgoStatus.frontWheelAngle', 0.0),
            sample.get('msg.nohCtrlOutput.targetStrAngle', 0.0),
            sample.get('msg.nohCtrlOutput.targetAcceleration', 0.0)
            ])# dim 1
            features.append(feature)#dim 0 a list to tensor with shape (5,9)
        features = torch.stack(features, dim=0)
        sequence2 = self.samples[idx+6]# +1 is special process for my case
        labels=torch.tensor([
        sequence2.get('msg.egoEgoStatus.yawRate', 0.0),  
        sequence2.get('msg.egoEgoStatus.linearSpeed', 0.0),
        sequence2.get('msg.egoEgoStatus.accerationX', 0.0),
        sequence2.get('msg.egoEgoStatus.accerationY', 0.0),
        sequence2.get('msg.egoEgoStatus.steerWheelAngle', 0.0),
        sequence2.get('msg.egoEgoStatus.steerWheelAngleRate', 0.0),
        sequence2.get('msg.egoEgoStatus.frontWheelAngle', 0.0)
        ])
        labels=labels.view(1,-1)# or .unsqueeze(0) (7) to (1,7)
        return features,labels 

我的数据是接受rosbag包的信息,打印出来的。具体数据和结构见上一篇文章。这里根据我数据的特点,把读取方法实现在了类的init中,传递了文件的路径读取信息。读取后的信息存放在了samples里。
dataset要求提供其他两个方法:len 和getitem。可以理解为读取一个list的长度和使用下标读取对应位置的数据。其中 len-6 是我选取数据的方法造成的:
marry me我初步选择了一个时间序列长度为5,如此我计划构建的输入shape为(batch_size,5,9)验证数据为(batch_size,1,7)。有关于我feature的选取也可以看上一节。我的输出计划只给一个时间点,故序列长度为1就好。
以下名字后()内为shape或维度,代码中sequence (5,9)sample为(9),list features 为(5,9),但注意,这里他是有5个tensor的list,而不是tensor使用stack在维度0上压栈。 关于维度在- rnn我的疑问有用几个例子研究了一下,简单来说,(2,3,4),那么2是最外面的方括号,依次向内,最外面的为dim0也就是2,依次向内dim1这里是3,dim2是4。同时dim0是空间维度,dim1是行,dim2是列。若具体到rnn的input,那他又是:(batch_size, seq_len, input_size) 。总之维度感觉理解好了,后面不容易出错,同时对数据有更感性的认识。

这里有一点 我尝试保持list的状态 把(5,9)的list放入dataload里 出来的数据集合一个batch的维度是(16,1,9),也就是我用append方法堆积的维度消失了,查询了一下,据说是dateloader 的作用导致的,故这里用常规的压制办法,pytorch的stack方法,具体用法可以见这个大哥的解释:大哥解释,我这里

features = torch.stack(features, dim=0)

就是在维度0上,目前这个维度代表的是最外的括号,即sequence_length。
同时这里还有一个关键操作,否则后面维度会不匹配

labels=labels.view(1,-1)# or .unsqueeze(0) (7) to (1,7)

这里label刚处理后,只是一个只有dim0的tensor,为了和inputshape匹配,我增加了一个维度,也就是把(7)变成了(1,7)。view 可以实现这个功能,-1代表保留原来的状态,1是加入了新的维度。用unsqueeze(0)=效果一样。这个操作在第 0 个维度dim0上增加了一个维度。换句话说,将一个包含七个元素的一维张量转换为一个包含一个一维张量的二维张量,其中新添加的维度的大小为 1。以行和列的角度来说,原来的行变为现在的列,行的大小是我们新加入的1。

于是我们这个模块的返回值为features(5,9),labels(1,7)

建立模型类(1)

(1:本节有错误,在更新里贴出来了矫正,为了突出错误问题,这里不改正)

class MyRNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.rnn = nn.RNN(input_size=9, hidden_size=32, num_layers=1, batch_first=True)
        for p in self.rnn.parameters():
            nn.init.normal_(p, mean=0.0, std=0.001) 
        self.linear = nn.Linear(32, 7)
        self.relu = nn.ReLU()
    def forward(self, x,hidden_prev):
        out, hidden_prev = self.rnn(x,hidden_prev)# out (16,5,32) #hidden(1,16,32)        
        # Get output from the last time step
        last_output = out[:, -1, :]#last_output(16,1,32)        
        # Feed into linear layer and apply activation
        logits = self.linear(last_output)#logits (16,1,7)
        output = self.relu(logits)#output(16,1,7)# this is my mistake        
        return output,hidden_prev

这里有了之前一节的内容,应该没有太多问题,有几点解释一下:

  • 建立rnn单元和linear层,后者是用来调整输出形状的,
   for p in self.rnn.parameters():
        nn.init.normal_(p, mean=0.0, std=0.001)

这里的主要目的是为了初始化模型参数,以使其在训练的初始阶段具有适当的随机性,从而有助于模型的训练。RNN 在初始化时可以使用正态分布初始化,以确保参数的初始值不会过大或过小,从而减少梯度爆炸或梯度消失的问题,RNN的结构,教程说容易发生梯度问题

  • out, hidden_prev 是rnn的两个输出,关于他俩的维度和含义以及关于rnn这里不再详细解释,在我开头给出的链接rnn疑问中,有所解释。前者是所有步骤的输出序列,后者是最后一个时序的隐藏状态 output (16,5,32) hidden_prev(1,16,32), output [:, -1, :] (16.1.32)),这里我们对于一个批次只关心每个序列的最终输出。其他步骤的解释和shape在代码中已给出。
  • 线型层的变换,不用担心shape变乱,线型层的操作不会对feature 也就是这里dim2 以外的层产生影响。
    我开始也担心出现问题,自己用例子实验过,但具体解释还是看gpt解释的好一些:

全连接层或线性层的基本操作是线性变换加上一个偏置项,假设输入是x,参数矩阵是W,偏置是b,那么输出形状是y = Wx + b。只要W的形状是(out_features, in_features),b的形状是(out_features,),那么输出y的形状就是(batch_size, out_features)。在全过程中,batch_size并没有发生改变,也就是说,线性层并不改变样本量。

其次,对于时间序列数据,全连接层通常是直接应用于每个时间步的数据,即对每个时间步的数据都进行相同的线性变换操作,因此不改变时间步维度。

故,全连接层处理的是每一个样本在特征维度上的数据,将一个样本的特征进行线性变换而不涉及到其他维度。它是独立的也就是说该层并不知道前一个样本或者后一个样本的存在。对于三维数据(batch_size, seq_len, in_features),全连接层会依次对每个样本的每个时间步的数据进行线性变换,因此不会改变dim0和dim1。

train

#hidden_prev init
def train_RNN(dataloader,model,loss_fn,optimizer):
    #hidden_prev init
    size = len(dataloader.dataset)
    # Set the model to training mode - important for batch normalization and dropout layers
    model.train()
    for batch, (X, y) in enumerate(dataloader):
        # print(X.shape, y.shape)
        # print (batch)
        batch_size=X.size(0)
        hidden_prev = torch.zeros(1, batch_size, 32)
        X = X.to(device)  # 将数据移到设备上
        y = y.to(device)  
        output, _ = model(X, hidden_prev)
        #hidden_prev = hidden_prev.detach()
        loss = loss_fn(output, y)
        model.zero_grad()
        loss.backward()
        optimizer.step()
        if batch % 50 == 0:# set print frequenz
            loss, current = loss.item(), (batch + 1) * len(X)
            print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")

这里基本和上一节一样,不详细解释了。唯一值的注意的是hidden_prev = hidden_prev.detach()
也就是时间步这个传递权重这个过程是对于一个input的时间维度内的,不应该在整个数据的不同batch间传播,要清理。而hidden_prev作为这一个批次的带着隐藏层形状的最后一个时间步的输出要给到下一个batch的数据中,并在下一个批次的第一个时间步被使用。这是一种常见的处理方式,特别是在处理连续的数据或序列时。但,每个批次的数据是完全独立的序列(例如不同的文本句子),则要在每个批次开始时重置隐藏状态,以避免不同序列之间的信息混淆。

这里再贴一段同样写在rnn问答文章里的gpt解释,我也是看了才悟了

隐藏状态的权重学习问题:在RNN中,隐藏状态确实需要在训练过程中更新。但是,这种更新是基于每个批次(或序列)的反向传播,而不是基于整个训练过程的累积梯度。每次传递一个新的输入序列到网络时,您都会使用当前的网络权重来计算隐藏状态和输出。然后,基于这个批次的损失,通过反向传播来更新网络的权重。
使用detach()的目的:在RNN中使用detach()是为了防止梯度在整个序列或多个序列之间传播。这有助于避免梯度消失或梯度爆炸问题,并减少计算负担。detach()并不会影响当前批次内的梯度反向传播,所以权重依然可以根据当前批次的误差进行更新。
hidden_prev.detach()确保了梯度不会在批次之间回溯,这是正确的。每当您调用detach()时,它会从当前计算图中分离出变量的历史,防止梯度回溯到之前的批次。这样,每个批次的数据都是独立处理的,它们不会影响到其他批次的梯度计算

main

我的main入口如下:

# main process        
if __name__ == "__main__":
    # build dataset 
    data_file_learning = 'training.txt' 
    data_file_t = 'test.txt' 
    custom_dataset_training = CustomDataset_RNN(data_file_learning)# (numbers,(features,labels)) features=(5,9) labels=(1,7)
    custom_dataset_valid = CustomDataset_RNN(data_file_t) 
    print(f"Number of samples in custom_dataset_training: {len(custom_dataset_training[0])}")# 2one sample: features and labels
    print(f"sequence length of features in one sample: {len(custom_dataset_training[0][0])}")# 5 sequence length of features
    print(f"sequence length of features in one sample: {len(custom_dataset_training[0][0][0])}")# 9 sequence length of features
    print(f"Number of features in one sample: {len(custom_dataset_training[0][1])}")# 1 sequence length of labels
    print(f"Number of features in one sample: {len(custom_dataset_training[0][1][0])}")# 7 sequence length of features

    # para
    learning_rate = 1e-3
    batch_size = 16
    epochs = 8000
    # data loader
    data_loader_training = DataLoader(custom_dataset_training, batch_size=batch_size, shuffle=True,drop_last=True)
    data_loader_valid = DataLoader(custom_dataset_valid, batch_size=batch_size,shuffle=True, drop_last=True)
    i=0
    for data_batch, label_batch in data_loader_training:
        i=i+1
        print(f"Shape of data_batch: {data_batch.shape}")#(16,5,9)

        for data, label in zip(data_batch, label_batch):
            print(f"Shape of data: {data.shape}")# (5,9)
            print(f"Shape of label: {label.shape} {label.dtype}")#(1,7)
        break
    #add device 
    device = (
        "cuda"
        if torch.cuda.is_available()
        else "mps"
        if torch.backends.mps.is_available()
        else "cpu"
    )
    # create model
    model =MyRNN().to(device) 
    print('model:\n',model)
    loss_fn = nn.MSELoss()  
    # create OP for BQ
    optimizer = optim.Adam(model.parameters(), learning_rate) 
    scheduler = StepLR(optimizer, step_size=100, gamma=0.9) 
    # #hidden_prev init
    # hidden_prev = torch.zeros(1, 16, 32)
    for iter in range(epochs):
        print(f'_________________Epoch:{iter+1}/{epochs}_______________________')
        train_RNN(data_loader_training,model,loss_fn,optimizer)
        test_RNN(data_loader_valid,model,loss_fn)
        scheduler.step()
    # saving model 
    torch.save(model.state_dict(), 'VehicalStateperML_RNN.pth')

主流程代码基本没有变化,见上一章解释在这里插入图片描述
我的数据和tensor的维度打印验证符合预期,见上图。

test

def test_RNN(dataloader, model, loss_fn):
    # Set the model to evaluation mode - important for batch normalization and dropout layers
    model.eval()
    size = len(dataloader.dataset)
    num_batches = len(dataloader)
    test_loss, correct = 0, 0
    hidden_prev = torch.zeros(1, 16, 32)
    # # Evaluating the model with torch.no_grad() ensures that no gradients are computed during test mode
    # # also serves to reduce unnecessary gradient computations and memory usage for tensors with requires_grad=True
    with torch.no_grad():
         for X, y in dataloader:
            batch_size=X.size(0)
            hidden_prev = torch.zeros(1, batch_size, 32)
            X = X.to(device)  # 将数据移到设备上
            y = y.to(device) 
            pred,_ = model(X,hidden_prev)
            test_loss += loss_fn(pred, y).item()
    #       #correct += (pred.argmax(1) == y).type(torch.float).sum().item()

    test_loss /= num_batches
    print(f"Avg loss: {test_loss:>8f} \n")

这里不解释了,同上一篇文章

run的过程如下:
在这里插入图片描述

问题请教大佬
我训练了8000次,但是和之前普通全连接神经网络loss收敛不同,今天早上来看,loss还是很大,不知道出现了什么问题哦?而且目测每一次的cost基本都没有下降 待我找到后下次更新,或者请大神帮我留言呀!

检查loss不下降问题记录:

检查loadloader后的数据是否有错误

在这里插入图片描述打印一个batch内的时序输入信息,对比原始数据,符合预期。pass

权重初始化 && 修改模型结构

    def __init__(self):
        super().__init__()
        self.rnn = nn.RNN(input_size=9, hidden_size=32, num_layers=1, batch_first=True) 
        self.linear = nn.Linear(32,32)
        self.relu = nn.ReLU()
        self.linear=nn.Linear(32,7)
        for name,param in self.named_parameters():
            if 'weight' in name:
                nn.init.normal_(param, mean=0.0, std=0.01)
            elif 'bias' in param:
                nn.init.constant_(param, 0)   

这里有一个问题,原来模型最后一层疏忽了,激活函数relu放在最后了,这样我的模型没有办法输出负数了,索性我再加一层linear。
其次我修改了初始化代码,把所有的参数都初始化 ,权重使用正态,同时提高标准差,是梯度初始化范围更大,同时把bias初始化为0

dataloader 随机问题

突然发现,在第一步验证的时候,我的第一个batch不是我原始数据的开头,也就是我在dataloader生成的时候开启了shuffle=True,在第一篇文章里有学习过,这里对于rnn的话不太好,因为我在代码里还传递了不同batch之间的hiddenstate(without grad),这里我依旧保持传递最后一个时间步的hiddenstate而选择关闭batch的随机

forward模型问题

千在意万在意,我的维度还是错了
原始代码如下:

    def forward(self, x,hidden_prev):
        out, hidden_prev = self.rnn(x,hidden_prev)# out (16,5,32) #hidden(1,16,32)        
        # Get output from the last time step
        last_output = out[:, -1, :]#last_output(16,1,32)        
        # Feed into linear layer and apply activation
        logits = self.linear(last_output)#logits (16,1,7)
        output = self.relu(logits)#output(16,1,7)        
        return output,hidden_prev

这里我的last_output 不是(16, 1, 32) 而是(16,32),所以我的output是 (16, 7)而不是(16,1,7),不知道为啥不会报错,但我这里修复一下,同时配合模型结构不再把激活函数relu放在最后

    def forward(self, x,hidden_prev):
        out, hidden_prev = self.rnn(x,hidden_prev)# out (16,5,32) #hidden(1,16,32)        
        # Get output from the last time step
        last_output = out[:, -1, :]#last_output(16,32)   
        last_output = last_output.unsqueeze(1)# let (16,32)to(16,1,32)     
        # Feed into linear layer and apply activation
        logits = self.linear(last_output)#logits (16,1,32)
        output_t = self.relu(logits)#output_t(16,1,32)  
        output =self.linear_f(output_t)#output(16,1,7)
        return output,hidden_prev

以上为修改后的forward。

同时我问了一下gpt为什么维度不对,没有发生bug

至于为什么原先维度不匹配的情况下代码仍然能够运行,这可能是由于您使用的损失函数或框架的某些特性。有些深度学习框架(如 PyTorch 或 TensorFlow)在计算损失时会自动尝试广播(broadcast)不匹配的维度。然而,这并不总是理想的,因为它可能导致意想不到的行为或难以调试的错误。因此,最好的做法是确保输入和标签的维度严格匹配。

所以感觉维度这东西,每写一步都要注意,写一个预期并打印。不知道前辈们有什么好的解决办法,避免维度错误?????

named_paramters()方法用来跟踪权重

示例来自我的gpt老师

import torch.nn as nn

class SimpleModel(nn.Module):
    def __init__(self):
        super(SimpleModel, self).__init__()
        self.fc1 = nn.Linear(10, 5)
        self.fc2 = nn.Linear(5, 3)

    def forward(self, x):
        x = self.fc1(x)
        x = self.fc2(x)
        return x

model = SimpleModel()

# 遍历模型的参数和它们的名称
for name, param in model.named_parameters():
    print(f"Parameter name: {name}")
    print(f"Parameter shape: {param.shape}")
    print(f"Parameter values: {param.data}")

如此还可以加入可视化来关注训练过程中 loss 权重 随epoch的变化,即使发现问题。

本次调整结果

结果貌似好了不少!未来继续调整,尤其是加入可视化,便于调整超参
在这里插入图片描述

dataloader shuffle=False

上面讨论过,由于我的rnn结构以及train中传递了不同batch之间的隐藏状态,必须要关闭dataloader的随机抽取。如此在训练中我发现了一个不好的问题,每一次个epoch 训练的数据是一毛一样的,我的cost不下降后,每一个epoch的每一个batch组之间的loss基本没有差距了
在这里插入图片描述在这里插入图片描述

所以这里有两个问题:1 需要开启随机,关闭batch之间的隐藏层传递看一下效果 2 为什么权重学习不动,打一下权重的过程变化

修改随机为开启,但是隐藏层状态不在batch间传递之后

在这里插入图片描述在这里插入图片描述在这里插入图片描述
上图是训练了2个小时之后的效果,发现确实loss收敛了,但是后期loss下降速度变慢,同时test loss要稍微大于 training loss了 。但是基本保证了目前模型和流程是ok的了,接下来重点调整超参数(大佬们怎么看?)

推理模型 数据验证

计划还是分为两个部分,单例模式测试验证,以及验证数据集合验证。
我的数据集分为三份,training和testing是一辆车的某一天,validation是同一车的另一次出行,确保动力学参数基本相同

首先是单例验证

在这里插入图片描述

用了一个公司实际试车(超豪华电动人工智能无敌可进化超跑)的实际数据,选了一个长度为5(t0-t4)的时序,上面红框是试车t5的实际结果,下面红框是外推结果。代码细节用到的知识前面都讲过不再重复

推理模型代码如下:代码的结尾是我使用的数据组,模型的参数也在附件中,大家使用下面的代码加参数就可以复现我的结果

import torch
from torch import nn

from rnnForYawrate import MyRNN
from rnnForYawrate import CustomDataset_RNN
from torch.utils.data import Dataset, DataLoader
# main process        
if __name__ == "__main__":
    data_file = 'validationData.txt'  
    custom_dataset_inference = CustomDataset_RNN(data_file)
    batch_size=16
    # 数据加载器
    data_loader_inference = DataLoader(custom_dataset_inference, batch_size=batch_size,shuffle=True, drop_last=True)
    device = (
        "cuda"
        if torch.cuda.is_available()
        else "mps"
        if torch.backends.mps.is_available()
        else "cpu"
    )
    model_f=MyRNN().to(device)
    model_f.load_state_dict(torch.load('VehicalStateperML_RNN.pth'))
    model_f.eval()

    if 1:
    # single data inference    
        #hidden_prev init
        hidden_prev = torch.zeros(1, 1, 32) #
        
        data_test_1=torch.tensor([0.03987893462181091,13.782072067260742,0.21904867887496948,0.8140702247619629,0.14955520629882812,
        0.0,0.01074754074215889,8.803881645202637,-0.25467321276664734])
        
        data_test_2=torch.tensor([0.038624390959739685,13.786596298217773,0.2508492171764374,0.8024824857711792,0.15127170085906982,
        0.0,0.010829867795109749,8.872920989990234,-0.3033924400806427])
        
        data_test_3=torch.tensor([0.03859284520149231,13.785704612731934,0.15484023094177246,0.8001524209976196,0.1524304449558258,
        0.0,0.010974112898111343,8.793074607849121,-0.34160029888153076])

        data_test_4=torch.tensor([0.03933088481426239,13.788601875305176,0.2645440995693207,0.8018520474433899,0.15475332736968994,
        0.0,0.01099490374326706,8.803157806396484,-0.4666106402873993])

        data_test_5=torch.tensor([0.039885520935058594,13.795905113220215,0.16176363825798035,0.7602496147155762,0.15518921613693237,
        0.0,0.011025872081518173,8.80613899230957,-0.5674195289611816])
        stacked_data = torch.stack((data_test_1, data_test_2, data_test_3, data_test_4, data_test_5)) #shape (5,9)
        stacked_data=stacked_data.unsqueeze(0)# shape (1,5,9)
        with torch.no_grad():
            stacked_data = stacked_data.to(device)  
            hidden_prev=hidden_prev.to(device)
            output,_ = model_f(stacked_data,hidden_prev)  # 使用模型进行预测
        print(output) 
    # else:
    # nulti data inference model

        # loss_fn = nn.MSELoss()
        # trainingModel.test_loop(data_loader_inference, model_f, loss_fn)

# RNN single data inference model
# msg.egoEgoStatus.yawRate: 0.03987893462181091
# msg.egoEgoStatus.linearSpeed: 13.782072067260742
# msg.egoEgoStatus.accerationX: 0.21904867887496948
# msg.egoEgoStatus.accerationY: 0.8140702247619629
# msg.egoEgoStatus.steerWheelAngle: 0.14955520629882812
# msg.egoEgoStatus.steerWheelAngleRate: 0.0
# msg.egoEgoStatus.frontWheelAngle: 0.010625587776303291
# msg.nohCtrlOutput.targetStrAngle: 8.803881645202637
# msg.nohCtrlOutput.targetAcceleration: -0.25467321276664734
# msg.egoEgoStatus.yawRate: 0.038624390959739685
# msg.egoEgoStatus.linearSpeed: 13.786596298217773
# msg.egoEgoStatus.accerationX: 0.23861587047576904
# msg.egoEgoStatus.accerationY: 0.8024824857711792
# msg.egoEgoStatus.steerWheelAngle: 0.15127170085906982
# msg.egoEgoStatus.steerWheelAngleRate: 0.0
# msg.egoEgoStatus.frontWheelAngle: 0.01074754074215889
# msg.nohCtrlOutput.targetStrAngle: 8.8500394821167
# msg.nohCtrlOutput.targetAcceleration: -0.3033924400806427
# msg.egoEgoStatus.yawRate: 0.03859284520149231
# msg.egoEgoStatus.linearSpeed: 13.785704612731934
# msg.egoEgoStatus.accerationX: 0.2508492171764374
# msg.egoEgoStatus.accerationY: 0.8001524209976196
# msg.egoEgoStatus.steerWheelAngle: 0.1524304449558258
# msg.egoEgoStatus.steerWheelAngleRate: 0.0
# msg.egoEgoStatus.frontWheelAngle: 0.010829867795109749
# msg.nohCtrlOutput.targetStrAngle: 8.872920989990234
# msg.nohCtrlOutput.targetAcceleration: -0.34160029888153076
# msg.egoEgoStatus.yawRate: 0.03933088481426239
# msg.egoEgoStatus.linearSpeed: 13.788601875305176
# msg.egoEgoStatus.accerationX: 0.2645440995693207
# msg.egoEgoStatus.accerationY: 0.8018520474433899
# msg.egoEgoStatus.steerWheelAngle: 0.15475332736968994
# msg.egoEgoStatus.steerWheelAngleRate: 0.0
# msg.egoEgoStatus.frontWheelAngle: 0.01099490374326706
# msg.nohCtrlOutput.targetStrAngle: 8.803157806396484
# msg.nohCtrlOutput.targetAcceleration: -0.4666106402873993
# msg.egoEgoStatus.yawRate: 0.039885520935058594
# msg.egoEgoStatus.linearSpeed: 13.795905113220215
# msg.egoEgoStatus.accerationX: 0.16176363825798035
# msg.egoEgoStatus.accerationY: 0.7602496147155762
# msg.egoEgoStatus.steerWheelAngle: 0.15518921613693237
# msg.egoEgoStatus.steerWheelAngleRate: 0.0
# msg.egoEgoStatus.frontWheelAngle: 0.011025872081518173
# msg.nohCtrlOutput.targetStrAngle: 8.80613899230957
# msg.nohCtrlOutput.targetAcceleration: -0.5674195289611816

#ans:
# msg.egoEgoStatus.yawRate: 0.04009075462818146
# msg.egoEgoStatus.linearSpeed: 13.800976753234863
# msg.egoEgoStatus.accerationX: 0.20156517624855042
# msg.egoEgoStatus.accerationY: 0.7818496227264404
# msg.egoEgoStatus.steerWheelAngle: 0.15529820322990417
# msg.egoEgoStatus.steerWheelAngleRate: 0.0
# msg.egoEgoStatus.frontWheelAngle: 0.011033616028726101

在数据集的范围内看起来效果不错,未来的调节方向主要放在超参数和收集的数据集本身上

超参数调整

首先需要可视化

更新在了第三篇文章中

小白认真学习认真求赞,不断进步!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值