【时间序列篇】基于LSTM的序列分类-Pytorch实现 part3 化为己用

系列文章目录

【时间序列篇】基于LSTM的序列分类-Pytorch实现 part1 案例复现
【时间序列篇】基于LSTM的序列分类-Pytorch实现 part2 自有数据集构建
【时间序列篇】基于LSTM的序列分类-Pytorch实现 part3 化为己用


在一个人体姿态估计的任务中,需要用深度学习模型来进行序列分类。

化为己用,实现成功。

文章目录

  • 系列文章目录
  • 前言
  • 一、模型训练
    • [1 导入库和自用函数](#1 导入库和自用函数)
    • [2 导入数据集](#2 导入数据集)
    • [3 设备部署](#3 设备部署)
    • [4 网络模型](#4 网络模型)
    • [5 训练过程](#5 训练过程)
    • [6 运行结果](#6 运行结果)
    • [7 完整代码](#7 完整代码)
    • SequenceClassifier
  • 二、模型预测
    • [1 完整代码](#1 完整代码)
    • [2 运行结果](#2 运行结果)
  • 三、模型评估
    • [1 完整代码](#1 完整代码)
    • [2 运行结果](#2 运行结果)
  • 总结

前言

结合了part1 和 part2的文章,处理现有序列分类任务。

基于LSTM的序列分类-Pytorch实现 这个部分先告一段落。

part3 主要是优化后的代码实现,包括训练,预测,模型评估。


一、模型训练

这一部分就是对part1文章中代码的优化和运行结果。每一节都是一整个代码的一部分,最后放完整代码。

1 导入库和自用函数

python 复制代码
import os
import copy
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
import torch.optim as optim
from tqdm import tqdm


def calculate_accuracy(y_pred, y_true):
    _, predicted_labels = torch.max(y_pred, 1)
    correct = (predicted_labels == y_true).float()
    accuracy = correct.sum() / len(correct)
    return accuracy

2 导入数据集

python 复制代码
'''
/****************************************************/
    导入数据集
/****************************************************/
'''
# ----------------------------------------------------#
#   路径指定
# ----------------------------------------------------#
ROOT_PATH = "DATA/RT_Position_dataset"

dataset_path = os.path.join(ROOT_PATH, "dataset")
target_path = os.path.join(ROOT_PATH, "groups/Movement4_target.csv")
groups_path = os.path.join(ROOT_PATH, "groups/Movement4_DatasetGroup.csv")

checkpoint_pth = "best_model.pth"  # 模型参数
seq_len = 16  # 序列长度
# ----------------------------------------------------#
#    sequences列表读取所有的样本
# ----------------------------------------------------#
path = os.path.join(dataset_path, "Movement4_")
sequences = list()
for i in range(1, 3731):  # 3731为样本数
    file_path = path + str(i) + '.csv'
    # print(file_path)
    df = pd.read_csv(file_path, header=0)
    values = df.values
    sequences.append(values)

# print(len(sequences))
# len_sequences = []
# for one_seq in sequences:
#     len_sequences.append(len(one_seq))
# ----------------------------------------------------#
#   数据集标签
# ----------------------------------------------------#
targets = pd.read_csv(target_path)
targets = targets.values[:, 1]
# ----------------------------------------------------#
#   数据集划分
# ----------------------------------------------------#
groups = pd.read_csv(groups_path, header=0)
groups = groups.values[:, 1]


# ----------------------------------------------------#
#   Padding the sequence with the values in last row to max length
# ----------------------------------------------------#
# 函数用于填充和截断序列
def pad_truncate_sequences(sequences, max_len, dim=4, truncating='post', padding='post'):
    # 初始化一个空的numpy数组,用于存储填充后的序列
    padded_sequences = np.zeros((len(sequences), max_len, dim))
    for i, one_seq in enumerate(sequences):
        if len(one_seq) > max_len:  # 截断
            if truncating == 'pre':
                padded_sequences[i] = one_seq[-max_len:]
            else:
                padded_sequences[i] = one_seq[:max_len]
        else:  # 填充
            padding_len = max_len - len(one_seq)
            to_concat = np.repeat(one_seq[-1], padding_len).reshape(dim, padding_len).transpose()
            if padding == 'pre':
                padded_sequences[i] = np.concatenate([to_concat, one_seq])
            else:
                padded_sequences[i] = np.concatenate([one_seq, to_concat])
    return padded_sequences


# ----------------------------------------------------#
#   设置序列长度
# ----------------------------------------------------#
# 使用自定义函数进行填充和截断
final_seq = pad_truncate_sequences(sequences, max_len=seq_len, dim=4, truncating='post', padding='post')

# 设置标签从 1~6 换为 0~5
targets = np.array(targets)
final_targets = targets - 1

# ----------------------------------------------------#
#   数据集划分
# ----------------------------------------------------#

# 将numpy数组转换为PyTorch张量
final_seq = torch.tensor(final_seq, dtype=torch.float)

# 划分样本为 训练集,验证集
train = [final_seq[i] for i in range(len(groups)) if groups[i] == 1]
validation = [final_seq[i] for i in range(len(groups)) if groups[i] == 2]
# 标签同理
train_target = [final_targets[i] for i in range(len(groups)) if groups[i] == 1]
validation_target = [final_targets[i] for i in range(len(groups)) if groups[i] == 2]

# 转换为PyTorch张量
train = torch.stack(train)
train_target = torch.tensor(train_target).long()

validation = torch.stack(validation)
validation_target = torch.tensor(validation_target).long()

3 设备部署

python 复制代码
'''
/****************************************************/
    device
/****************************************************/
'''

def device_on():
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    print(f"Using {device} device")
    return device


# ----------------------------------------------------#
# device
# ----------------------------------------------------#
device = device_on()

4 网络模型

python 复制代码
'''
/****************************************************/
    网络模型
/****************************************************/
'''


# ----------------------------------------------------#
#   创建模型
# ----------------------------------------------------#
class TimeSeriesClassifier(nn.Module):
    def __init__(self, n_features, hidden_dim=256, output_size=1):
        super().__init__()
        self.lstm = nn.LSTM(input_size=n_features, hidden_size=hidden_dim, batch_first=True)
        self.fc = nn.Linear(hidden_dim, output_size)  # output_size classes

    def forward(self, x):
        x, _ = self.lstm(x)  # LSTM层
        x = x[:, -1, :]  # 只取LSTM输出中的最后一个时间步
        x = self.fc(x)  # 通过一个全连接层
        return x


# ----------------------------------------------------#
#   模型实例化
# ----------------------------------------------------#
seq_len = 16  # 根据你的序列长度进行调整
n_features = 4  # 根据你的特征数量进行调整
output_size = 6
model = TimeSeriesClassifier(n_features=n_features, output_size=output_size)

# # 打印模型结构
print(model)
# ----------------------------------------------------#
#   模型部署
# ----------------------------------------------------#
model.to(device)

5 训练过程

python 复制代码
'''
/****************************************************/
    训练过程
/****************************************************/
'''

# 设置训练参数
epochs = 100  # 训练轮数,根据需要进行调整
batch_size = 4  # 批大小,根据你的硬件调整

# DataLoader 加载数据集
train_dataset = torch.utils.data.TensorDataset(train, train_target)
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)

validation_dataset = torch.utils.data.TensorDataset(validation, validation_target)
validation_loader = torch.utils.data.DataLoader(dataset=validation_dataset, batch_size=batch_size, shuffle=True)

# 定义损失函数和优化器
criterion = nn.CrossEntropyLoss()
criterion = criterion.to(device)

# 学习率和优化策略
learning_rate = 1e-3
optimizer = optim.Adam(params=model.parameters(), lr=learning_rate, weight_decay=5e-4)
lr_scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=2, gamma=0.5)  # 设置学习率下降策略

# ----------------------------------------------------#
#   训练
# ----------------------------------------------------#
best_acc = 0.0
for epoch in range(epochs):
    model.train()  # 将模型设置为训练模式
    train_epoch_loss = []
    train_epoch_accuracy = []
    pbar = tqdm(train_loader, total=len(train_loader))
    for index, (inputs, labels) in enumerate(pbar, start=1):
        # 获取输入数据和目标,并将它们转移到GPU(如果可用)
        inputs = inputs.to(device)
        labels = labels.to(device)
        # 清零梯度
        optimizer.zero_grad()
        # 前向传播
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        train_epoch_loss.append(loss.item())
        accuracy = calculate_accuracy(outputs, labels)
        train_epoch_accuracy.append(accuracy.item())

        pbar.set_description(f'Epoch [{epoch + 1}/{epochs}]')
        pbar.set_postfix(**{'loss': loss.item(),
                            'accuracy': accuracy.item(),
                            })

    # Validation accuracy
    model.eval()
    valid_epoch_loss = []
    valid_epoch_accuracy = []
    pbar = tqdm(validation_loader, total=len(validation_loader))
    for index, (inputs, labels) in enumerate(pbar, start=1):
        inputs = inputs.to(device)
        labels = labels.to(device)
        outputs = model(inputs)
        loss = criterion(outputs, labels)

        valid_epoch_loss.append(loss.item())
        accuracy = calculate_accuracy(outputs, labels)
        valid_epoch_accuracy.append(accuracy.item())
        pbar.set_description('valid')
        pbar.set_postfix(**{'total_loss': loss.item(),
                            'accuracy': accuracy.item(),
                            })
    # 计算平均精度
    print("--------------------------------------------")
    train_epoch_loss = np.average(train_epoch_loss)
    train_epoch_accuracy = np.average(train_epoch_accuracy)
    print(f'Epoch {epoch + 1}, train Accuracy: {train_epoch_accuracy:.4f}')
    valid_epoch_loss = np.average(valid_epoch_loss)
    valid_epoch_accuracy = np.average(valid_epoch_accuracy)
    print(f'Epoch {epoch + 1}, Validation Accuracy: {valid_epoch_accuracy:.4f}')
    print("--------------------------------------------")
    if valid_epoch_accuracy > best_acc:
        best_acc = valid_epoch_accuracy
        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, checkpoint_pth)

print('Finished Training')
print('Best val Acc: {:4f}'.format(best_acc))

6 运行结果


可以看到相比起part1的实验,分类准确率更高。毕竟part1总共314个样本,自有数据集样本有3730个。

7 完整代码

SequenceClassifier

二、模型预测

输入样本,运行模型,输出预测

1 完整代码

这里直接贴完整代码。

python 复制代码
"""
@file name:predict.py
@desc: 用于序列分类预测
"""

import os
import pandas as pd
import torch
import torch.nn as nn

'''
/****************************************************/
    输入一个数据样本
/****************************************************/
'''
# 读取CSV样本文件
csv_file = "DATA/RT_Position_dataset/dataset/Movement4_7.csv"
data = pd.read_csv(csv_file)

# 将Pandas DataFrame转换为NumPy数组
data_array = data.values

# 将NumPy数组转换为PyTorch张量
input = torch.tensor(data_array, dtype=torch.float).unsqueeze(0)

'''
/****************************************************/
    device
/****************************************************/
'''

def device_on():
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    print(f"Using {device} device")
    return device


# ----------------------------------------------------#
# device
# ----------------------------------------------------#
device = device_on()

'''
/****************************************************/
    网络模型
/****************************************************/
'''

# ----------------------------------------------------#
#   创建模型
# ----------------------------------------------------#
class TimeSeriesClassifier(nn.Module):
    def __init__(self, n_features, hidden_dim=256, output_size=1):
        super().__init__()
        self.lstm = nn.LSTM(input_size=n_features, hidden_size=hidden_dim, batch_first=True)
        self.fc = nn.Linear(hidden_dim, output_size)  # output_size classes

    def forward(self, x):
        x, _ = self.lstm(x)  # LSTM层
        x = x[:, -1, :]  # 只取LSTM输出中的最后一个时间步
        x = self.fc(x)  # 通过一个全连接层
        return x


# ----------------------------------------------------#
#   模型实例化
# ----------------------------------------------------#
seq_len = 16  # 根据你的序列长度进行调整
n_features = 4  # 根据你的特征数量进行调整
output_size = 6
model = TimeSeriesClassifier(n_features=n_features, output_size=output_size)

# ----------------------------------------------------#
#   模型部署
# ----------------------------------------------------#
model.to(device)

# ----------------------------------------------------#
#   导入模型参数
# ----------------------------------------------------#
save_pth = "best_model.pth"
checkpoint = torch.load(save_pth)
best_acc = checkpoint['best_acc']
model.load_state_dict(checkpoint['state_dict'])
'''
/****************************************************/
    预测输出
/****************************************************/
'''
model.eval()  # 设置为评估模式

# 假设你有一个预处理好的序列数据,shape为(batch_size, seq_len, n_features)
# 例如:一个序列,长度为16,每个时间步有4个特征
# input = torch.randn(1, 16, 4)  # 替换为你的数据

with torch.no_grad():
    output = model(input)
    # 将输出转换为概率分布
    prediction = torch.softmax(output, dim=1)
    # 取得最高概率的类别作为预测结果
    predicted_class = torch.argmax(prediction, dim=1)
print(f"Predicted class: {predicted_class}")

2 运行结果


三、模型评估

1 完整代码

python 复制代码
"""
@file name:evaluate.py
@desc: 用于模型评估
"""

import os
import numpy as np
import pandas as pd
import torch
import torch.nn as nn
from sklearn.metrics import accuracy_score, confusion_matrix, precision_score, recall_score, f1_score


def calculate_accuracy(y_pred, y_true):
    _, predicted_labels = torch.max(y_pred, 1)
    correct = (predicted_labels == y_true).float()
    accuracy = correct.sum() / len(correct)
    return accuracy


'''
/****************************************************/
    导入数据集
/****************************************************/
'''
# ----------------------------------------------------#
#   路径指定
# ----------------------------------------------------#
ROOT_PATH = "DATA/RT_Position_dataset"

dataset_path = os.path.join(ROOT_PATH, "dataset")
target_path = os.path.join(ROOT_PATH, "groups/Movement4_target.csv")
groups_path = os.path.join(ROOT_PATH, "groups/Movement4_DatasetGroup.csv")

checkpoint_pth = "best_model.pth"  # 模型参数
seq_len = 16  # 序列长度
# ----------------------------------------------------#
#    sequences列表读取所有的样本
# ----------------------------------------------------#
path = os.path.join(dataset_path, "Movement4_")
sequences = list()
for i in range(1, 3731):  # 3731为样本数
    file_path = path + str(i) + '.csv'
    # print(file_path)
    df = pd.read_csv(file_path, header=0)
    values = df.values
    sequences.append(values)

# print(len(sequences))
# len_sequences = []
# for one_seq in sequences:
#     len_sequences.append(len(one_seq))
# ----------------------------------------------------#
#   数据集标签
# ----------------------------------------------------#
targets = pd.read_csv(target_path)
targets = targets.values[:, 1]
# ----------------------------------------------------#
#   数据集划分
# ----------------------------------------------------#
groups = pd.read_csv(groups_path, header=0)
groups = groups.values[:, 1]


# ----------------------------------------------------#
#   Padding the sequence with the values in last row to max length
# ----------------------------------------------------#
# 函数用于填充和截断序列
def pad_truncate_sequences(sequences, max_len, dim=4, truncating='post', padding='post'):
    # 初始化一个空的numpy数组,用于存储填充后的序列
    padded_sequences = np.zeros((len(sequences), max_len, dim))
    for i, one_seq in enumerate(sequences):
        if len(one_seq) > max_len:  # 截断
            if truncating == 'pre':
                padded_sequences[i] = one_seq[-max_len:]
            else:
                padded_sequences[i] = one_seq[:max_len]
        else:  # 填充
            padding_len = max_len - len(one_seq)
            to_concat = np.repeat(one_seq[-1], padding_len).reshape(dim, padding_len).transpose()
            if padding == 'pre':
                padded_sequences[i] = np.concatenate([to_concat, one_seq])
            else:
                padded_sequences[i] = np.concatenate([one_seq, to_concat])
    return padded_sequences


# ----------------------------------------------------#
#   设置序列长度
# ----------------------------------------------------#
# 使用自定义函数进行填充和截断
final_seq = pad_truncate_sequences(sequences, max_len=seq_len, dim=4, truncating='post', padding='post')

# 设置标签从 1~6 换为 0~5
targets = np.array(targets)
final_targets = targets - 1

# ----------------------------------------------------#
#   数据集划分
# ----------------------------------------------------#

# 将numpy数组转换为PyTorch张量
final_seq = torch.tensor(final_seq, dtype=torch.float)

# 划分样本为 训练集,验证集
train = [final_seq[i] for i in range(len(groups)) if groups[i] == 1]
validation = [final_seq[i] for i in range(len(groups)) if groups[i] == 2]
# 标签同理
train_target = [final_targets[i] for i in range(len(groups)) if groups[i] == 1]
validation_target = [final_targets[i] for i in range(len(groups)) if groups[i] == 2]

# 转换为PyTorch张量
train = torch.stack(train)
train_target = torch.tensor(train_target).long()

validation = torch.stack(validation)
validation_target = torch.tensor(validation_target).long()

'''
/****************************************************/
    device
/****************************************************/
'''


def device_on():
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    print(f"Using {device} device")
    return device


# ----------------------------------------------------#
# device
# ----------------------------------------------------#
device = device_on()

'''
/****************************************************/
    网络模型
/****************************************************/
'''


# ----------------------------------------------------#
#   创建模型
# ----------------------------------------------------#
class TimeSeriesClassifier(nn.Module):
    def __init__(self, n_features, hidden_dim=256, output_size=1):
        super().__init__()
        self.lstm = nn.LSTM(input_size=n_features, hidden_size=hidden_dim, batch_first=True)
        self.fc = nn.Linear(hidden_dim, output_size)  # output_size classes

    def forward(self, x):
        x, _ = self.lstm(x)  # LSTM层
        x = x[:, -1, :]  # 只取LSTM输出中的最后一个时间步
        x = self.fc(x)  # 通过一个全连接层
        return x


# ----------------------------------------------------#
#   模型实例化
# ----------------------------------------------------#
seq_len = 16  # 根据你的序列长度进行调整
n_features = 4  # 根据你的特征数量进行调整
output_size = 6
model = TimeSeriesClassifier(n_features=n_features, output_size=output_size)

# # 打印模型结构
print(model)
# ----------------------------------------------------#
#   模型部署
# ----------------------------------------------------#
model.to(device)

# ----------------------------------------------------#
#   导入模型参数
# ----------------------------------------------------#
checkpoint_pth = "best_model.pth"
checkpoint = torch.load(checkpoint_pth)
best_acc = checkpoint['best_acc']
model.load_state_dict(checkpoint['state_dict'])
batch_size = 4

# DataLoader 加载数据集
train_dataset = torch.utils.data.TensorDataset(train, train_target)
train_loader = torch.utils.data.DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)

validation_dataset = torch.utils.data.TensorDataset(validation, validation_target)
validation_loader = torch.utils.data.DataLoader(dataset=validation_dataset, batch_size=batch_size, shuffle=True)

'''
/****************************************************/
    模型评估
/****************************************************/
'''
# ----------------------------------------------------#
#   评估
# ----------------------------------------------------#

# Validation
model.eval()
# 存储所有预测和真实标签
all_preds = []
all_labels = []
# 不计算梯度,减少计算和内存消耗
with torch.no_grad():
    for data, labels in validation_loader:
        # data和labels的预处理(如:转移到GPU、标准化等)...

        # 生成预测并获取最可能的类别
        outputs = model(data)
        _, predicted = torch.max(outputs, 1)

        # 收集预测和真实标签
        all_preds.extend(predicted.cpu().numpy())
        all_labels.extend(labels.cpu().numpy())

# 计算性能指标
accuracy = accuracy_score(all_labels, all_preds)
conf_matrix = confusion_matrix(all_labels, all_preds)
precision = precision_score(all_labels, all_preds, average='macro')
recall = recall_score(all_labels, all_preds, average='macro')
f1 = f1_score(all_labels, all_preds, average='macro')

print(f"Accuracy: {accuracy}")
print(f"Confusion Matrix:\n{conf_matrix}")
print(f"Precision: {precision}")
print(f"Recall: {recall}")
print(f"F1 Score: {f1}")

2 运行结果


总结

到此,基于LSTM的序列分类任务就结束啦。

相比于CNN图像处理,序列分类任务训练是非常快的。

正如李沐老师说的:
"文本是廉价的数据。"

完整项目
SequenceClassifier

相关推荐
C7211BA2 小时前
使用knn算法对iris数据集进行分类
算法·分类·数据挖掘
紫钺-高山仰止2 小时前
【脑机接口】脑机接口性能的电压波形的尖峰分类和阈值比较
大数据·分类·数据挖掘
5pace3 小时前
PyTorch深度学习快速入门教程【土堆】基础知识篇
人工智能·pytorch·深度学习
AI完全体6 小时前
AI小项目4-用Pytorch从头实现Transformer(详细注解)
人工智能·pytorch·深度学习·机器学习·语言模型·transformer·注意力机制
吉小雨8 小时前
PyTorch经典模型
人工智能·pytorch·python
crownyouyou10 小时前
最简单的一文安装Pytorch+CUDA
人工智能·pytorch·python
青椒大仙KI1116 小时前
24/9/19 算法笔记 kaggle BankChurn数据分类
笔记·算法·分类
artificiali1 天前
Anaconda配置pytorch的基本操作
人工智能·pytorch·python
#include<菜鸡>1 天前
动手学深度学习(pytorch土堆)-04torchvision中数据集的使用
人工智能·pytorch·深度学习
勤劳兔码农1 天前
文本分类实战项目:如何使用NLP构建情感分析模型
自然语言处理·分类·数据挖掘