系列文章目錄
【時(shí)間序列篇】基于LSTM的序列分類-Pytorch實(shí)現(xiàn) part1 案例復(fù)現(xiàn)
【時(shí)間序列篇】基于LSTM的序列分類-Pytorch實(shí)現(xiàn) part2 自有數(shù)據(jù)集構(gòu)建
【時(shí)間序列篇】基于LSTM的序列分類-Pytorch實(shí)現(xiàn) part3 化為己用
本篇文章是對(duì)已有一篇文章的整理歸納,并對(duì)文章中提及的模型用Pytorch實(shí)現(xiàn)。
前言
序列,可以是采樣得到的信號(hào)樣本,也可以是傳感器數(shù)據(jù)。
對(duì)于序列分類任務(wù),常用的思路有兩種:
1、原理統(tǒng)計(jì)相關(guān),分解序列的相關(guān)性質(zhì)研究規(guī)律(人工設(shè)計(jì)特征,再分類)
2、數(shù)據(jù)挖掘思路,機(jī)器學(xué)習(xí)做特征工程,模型擬合(自動(dòng)學(xué)習(xí)特征,再分類)
-
人工設(shè)計(jì)特征方法:
基于序列距離:計(jì)算距離進(jìn)行分類(類別模板or聚類)
基于統(tǒng)計(jì)特征:時(shí)序特征提取 (均值,方差,差分)再分類 -
自動(dòng)學(xué)習(xí)特征方法:
深度學(xué)習(xí)端到端(RNN, LSTM)
本文通過LSTM來實(shí)現(xiàn)對(duì)序列信號(hào)的分類。
主要思想和代碼框架來自參考文獻(xiàn)[1]
一、任務(wù)問題和數(shù)據(jù)集
1 任務(wù)問題
人體運(yùn)動(dòng)估計(jì):
傳感器生成高頻數(shù)據(jù),對(duì)不同狀態(tài)下采集的數(shù)據(jù)進(jìn)行分類,可以識(shí)別其范圍內(nèi)對(duì)象的移動(dòng)。通過設(shè)置多個(gè)傳感器并對(duì)信號(hào)進(jìn)行采樣分析,可以識(shí)別物體的運(yùn)動(dòng)方向。
“ 室內(nèi)用戶運(yùn)動(dòng)預(yù)測(cè) ”問題:
在該任務(wù)中,多個(gè)運(yùn)動(dòng)傳感器被放置在不同房間中,目標(biāo)基于運(yùn)動(dòng)傳感器捕獲的數(shù)據(jù)來識(shí)別個(gè)體是否已經(jīng)移動(dòng)穿過房間。
兩個(gè)房間有四個(gè)運(yùn)動(dòng)傳感器(A1,A2,A3,A4)。
下圖說明了傳感器在每個(gè)房間中的位置。
一個(gè)人可以沿著上圖中所示的六個(gè)預(yù)定義路徑中的任何一個(gè)移動(dòng)。每個(gè)路徑都生成一個(gè) RSS 測(cè)量的軌跡樣本,從軌跡的開始一直到標(biāo)記點(diǎn),在圖中表示為 M。標(biāo)記 M 對(duì)于所有運(yùn)動(dòng)都是相同的,因此不能僅僅根據(jù)在 M 處收集的 RSS 值來區(qū)分不同的路徑。
該圖還顯示了所考慮的用戶軌跡類型的簡(jiǎn)化說明,直線路徑導(dǎo)致于空間變化,曲線路徑導(dǎo)致空間不變。有在房間內(nèi)移動(dòng)和在房間之間移動(dòng)兩種類別。
2 數(shù)據(jù)集
文件 | 含義 |
---|---|
RSS_Position_dataset/dataset | 樣本數(shù)據(jù) |
RSS_Position_dataset/groups | 標(biāo)簽文件和組別文件(劃分?jǐn)?shù)據(jù)集) |
RSS_Position_dataset/MovementAAL.jpg | 上面的示意圖 |
數(shù)據(jù)集最重要的有316個(gè)csv文件:
- 【dataset 文件夾】
314 個(gè)MovementAAL csv文件,是序列樣本,每個(gè)文件都包含與輸入 RSS 數(shù)據(jù)的一個(gè)序列數(shù)據(jù)(每個(gè)文件記錄一個(gè)用戶軌跡)。該數(shù)據(jù)集包含314個(gè)序列數(shù)據(jù)(樣本csv文件)。
1個(gè) MovementAAL_target.csv 文件,是每個(gè)MovementAAL文件對(duì)應(yīng)的標(biāo)簽(類別)。每一個(gè)樣本對(duì)應(yīng)的類別,表明用戶的軌跡是否會(huì)導(dǎo)致空間變化(例如房間的變化)。特別地,標(biāo)簽為+1與位置變化相關(guān)聯(lián),而標(biāo)簽為 -1與位置保留相關(guān)聯(lián)。 - 【groups 文件夾】
MovementAAL_DatasetGroup.csv文件,用于劃分?jǐn)?shù)據(jù)集
3 數(shù)據(jù)集讀取并展示
import pandas as pd
# ----------------------------------------------------#
# 路徑指定,文件讀取
# ----------------------------------------------------#
df1 = pd.read_csv("DATA/RSS_Position_dataset/dataset/MovementAAL_RSS_1.csv")
df2 = pd.read_csv("DATA/RSS_Position_dataset/dataset/MovementAAL_RSS_2.csv")
df1.head() # 返回一個(gè)新的DataFrame或Series對(duì)象,默認(rèn)返回前5行。
df1.shape # 返回文件的size,不同文件的len(行數(shù))不同
二、模型實(shí)現(xiàn)
import os
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import torch
import torch.nn as nn
import torch.optim as optim
1 數(shù)據(jù)導(dǎo)入
'''
/****************************************************/
導(dǎo)入數(shù)據(jù)集
/****************************************************/
'''
# ----------------------------------------------------#
# 數(shù)據(jù)集樣本
# ----------------------------------------------------#
path = "DATA/RSS_Position_dataset/dataset/MovementAAL_RSS_"
sequences = list()
for i in range(1, 315): # 315為樣本數(shù)
file_path = path + str(i) + '.csv'
df = pd.read_csv(file_path, header=0)
values = df.values
sequences.append(values)
# ----------------------------------------------------#
# 數(shù)據(jù)集標(biāo)簽
# ----------------------------------------------------#
targets = pd.read_csv('DATA/RSS_Position_dataset/dataset/MovementAAL_target.csv')
targets = targets.values[:, 1]
# ----------------------------------------------------#
# 數(shù)據(jù)集劃分
# ----------------------------------------------------#
groups = pd.read_csv('DATA/RSS_Position_dataset/groups/MovementAAL_DatasetGroup.csv', header=0)
groups = groups.values[:, 1]
分析:
- 數(shù)據(jù)集樣本:將所有的樣本讀入sequences列表中,列表長(zhǎng)度為樣本數(shù),列表中每一個(gè)元素為一個(gè)樣本。
- 數(shù)據(jù)集標(biāo)簽:targets 中存放。
- 數(shù)據(jù)集劃分:數(shù)據(jù)集是在三對(duì)不同的房間中收集的,因此有三組。此信息可用于將數(shù)據(jù)集劃分為訓(xùn)練集,測(cè)試集和驗(yàn)證集。
2 數(shù)據(jù)預(yù)處理
由于時(shí)間序列數(shù)據(jù)的長(zhǎng)度不同,sequences列表中每個(gè)元素長(zhǎng)度不一。無法直接在此數(shù)據(jù)集上構(gòu)建模型。需要統(tǒng)一。原文中的思想是填充使相等。
這里是對(duì)樣本,即sequences列表變量進(jìn)行處理。
# ----------------------------------------------------#
# Padding the sequence with the values in last row to max length
# ----------------------------------------------------#
# 函數(shù)用于填充和截?cái)嘈蛄?/span>
def pad_truncate_sequences(sequences, max_len, dim=4, truncating='post', padding='post'):
# 初始化一個(gè)空的numpy數(shù)組,用于存儲(chǔ)填充后的序列
padded_sequences = np.zeros((len(sequences), max_len, dim))
for i, one_seq in enumerate(sequences):
if len(one_seq) > max_len: # 截?cái)?/span>
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
# 使用自定義函數(shù)進(jìn)行填充和截?cái)?/span>
seq_len = 32
# truncate or pad the sequence to seq_len
final_seq = pad_truncate_sequences(sequences, max_len=seq_len, dim=4, truncating='post', padding='post')
對(duì)數(shù)據(jù)集來說,標(biāo)簽 +1/-1 不利于模型輸出,變?yōu)?1/0。
這里是對(duì)標(biāo)簽,即targets類別變量進(jìn)行處理。
# 設(shè)置標(biāo)簽從 +1/-1 ,變?yōu)?1/0
targets = np.array(targets)
final_targets = (targets+1)/2
3 數(shù)據(jù)集劃分
# ----------------------------------------------------#
# 數(shù)據(jù)集劃分
# ----------------------------------------------------#
# 將numpy數(shù)組轉(zhuǎn)換為PyTorch張量
final_seq = torch.tensor(final_seq, dtype=torch.float)
# 劃分樣本為 訓(xùn)練集,驗(yàn)證集 和 測(cè)試集
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]
test = [final_seq[i] for i in range(len(groups)) if groups[i] == 3]
# 標(biāo)簽同理
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]
test_target = [final_targets[i] for i in range(len(groups)) if groups[i] == 3]
# 轉(zhuǎn)換為PyTorch張量
train = torch.stack(train)
train_target = torch.tensor(train_target).long()
validation = torch.stack(validation)
validation_target = torch.tensor(validation_target).long()
test = torch.stack(test)
test_target = torch.tensor(test_target).long()
4 網(wǎng)絡(luò)模型及實(shí)例化
'''
/****************************************************/
網(wǎng)絡(luò)模型
/****************************************************/
'''
# ----------------------------------------------------#
# LSTM 模型
# ----------------------------------------------------#
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輸出中的最后一個(gè)時(shí)間步
x = self.fc(x) # 通過一個(gè)全連接層
return x
# ----------------------------------------------------#
# 模型實(shí)例化 和 部署
# ----------------------------------------------------#
n_features = 4 # 根據(jù)你的特征數(shù)量進(jìn)行調(diào)整
output_size = 2
model = TimeSeriesClassifier(n_features=n_features, output_size=output_size)
# 打印模型結(jié)構(gòu)
print(model)
5 訓(xùn)練過程
'''
/****************************************************/
訓(xùn)練過程
/****************************************************/
'''
# 設(shè)置訓(xùn)練參數(shù)
epochs = 100 # 訓(xùn)練輪數(shù),根據(jù)需要進(jìn)行調(diào)整
batch_size = 4 # 批大小,根據(jù)你的硬件調(diào)整
# DataLoader 加載數(shù)據(jù)集
# 將數(shù)據(jù)集轉(zhuǎn)換為張量并創(chuàng)建數(shù)據(jù)加載器
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)
# 定義損失函數(shù)和優(yōu)化器
criterion = nn.CrossEntropyLoss()
# 學(xué)習(xí)率和優(yōu)化策略
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) # 設(shè)置學(xué)習(xí)率下降策略
# ----------------------------------------------------#
# 訓(xùn)練
# ----------------------------------------------------#
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
for epoch in range(epochs):
model.train() # 將模型設(shè)置為訓(xùn)練模式
train_epoch_loss = []
train_epoch_accuracy = []
for i, data in enumerate(train_loader, 0):
inputs, labels = data # 獲取輸入數(shù)據(jù)和標(biāo)簽
optimizer.zero_grad() # 清零梯度
outputs = model(inputs) # 前向傳播
loss = criterion(outputs, labels)
loss.backward() # 反向傳播和優(yōu)化
optimizer.step()
# 打印統(tǒng)計(jì)信息
# train_epoch_loss.append(loss.item())
# accuracy = calculate_accuracy(outputs, labels)
# train_epoch_accuracy.append(accuracy.item())
#
# train_running_loss = np.average(train_epoch_loss)
# train_running_accuracy = np.average(train_epoch_accuracy)
#
# if i % 10 == 9: # 每10個(gè)批次打印一次
# print("--------------------------------------------")
# print(f'Epoch {epoch + 1}, Loss: {train_running_loss}, accuracy: {train_running_accuracy}')
# Validation accuracy
model.eval()
valid_epoch_accuracy = []
with torch.no_grad():
for inputs, labels in validation_loader: # Assuming validation_loader is defined
outputs = model(inputs)
accuracy = calculate_accuracy(outputs, labels)
valid_epoch_accuracy.append(accuracy.item())
# 計(jì)算平均精度
valid_running_accuracy = np.average(valid_epoch_accuracy)
print(f'Epoch {epoch + 1}, Validation Accuracy: {valid_running_accuracy:.4f}')
print('Finished Training')
三、總結(jié)
在驗(yàn)證集上的分類準(zhǔn)確率最高才70%。emmm我猜是數(shù)據(jù)少。文章來源:http://www.zghlxwxcb.cn/news/detail-819991.html
CSDN: 進(jìn)行時(shí)間序列分類實(shí)踐–python實(shí)戰(zhàn)文章來源地址http://www.zghlxwxcb.cn/news/detail-819991.html
到了這里,關(guān)于【時(shí)間序列篇】基于LSTM的序列分類-Pytorch實(shí)現(xiàn) part1 案例復(fù)現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!