如何设计神经网络来进行突破性检测[已关闭]

问题描述 投票:0回答:1

摘要:

我正在设计一个用于大鼠开颅手术的自动钻孔系统。我希望当神经网络检测到突破时钻头会停止。照片为该系统。 System Overview Surgical results

我尝试的:

  1. 力传感器和加速度计实时采集信号(采样率为3kHz)。所以我用它们记录了一些钻孔过程(以恒定的进给速度和转速),并用Python将它们绘制出来,如下所示:Force data(蓝色代表原始数据,橙色代表黄油低通滤波数据)。vibrationdata from NIDAQmx
  2. 我希望使用神经网络来进行检测(因为我可以轻松地识别图中“forcedata”中蓝色的突破点)。所以我训练一个CNN-LSTM,输入0.1秒内收集到的数据,输出一个0-1的数字,其中0表示不钻,1表示钻。(因为我认为突破点是1和0的交界处)。所以我手动将数据分为标签为1的数据集和标签为0的数据集如图所示。red represents drilling process. label 1 sets are made by spliting the districts every 0.1s
  3. CNN-LSTM网络代码如下:
class CNNLSTMClassifier(nn.Module):
    def __init__(self, hidden_size, num_layers):
        super(CNNLSTMClassifier, self).__init__()
        self.cnn = nn.Sequential(
            nn.Conv1d(in_channels=1, out_channels=64, kernel_size=4, padding=1),
            nn.ReLU(),
            nn.MaxPool1d(kernel_size=2, stride=2),
            nn.Conv1d(in_channels=64, out_channels=128, kernel_size=4, padding=1),
            nn.ReLU(),
            nn.MaxPool1d(kernel_size=2, stride=2),
            nn.Conv1d(in_channels=128, out_channels=256, kernel_size=4, padding=1),
            nn.ReLU(),
            nn.MaxPool1d(kernel_size=2, stride=2)
        )
        self.lstm = nn.LSTM(input_size=256, hidden_size=hidden_size, num_layers=num_layers, batch_first=True)
        self.fc = nn.Linear(hidden_dim, 1)
        self.sigmoid = nn.Sigmoid() 

    def forward(self, x):
        x = x.unsqueeze(1)
        x = self.cnn(x)
        x = x.permute(0, 2, 1)  
        lstm_out, _ = self.lstm(x)
        out = self.fc(lstm_out[:, -1, :])  
        out = self.sigmoid(out)
        return out

我明白了

2024-03-18 16:25:50.708972 Epoch 1, Training loss 0.6764530851605625
2024-03-18 16:25:55.804250 Epoch 5, Training loss 0.6663112645497138
2024-03-18 16:26:02.211953 Epoch 10, Training loss 0.660625655507836
..................................................................
2024-03-18 16:28:04.148844 Epoch 100, Training loss 0.6572404681613006
Accuracy train: 0.63
Accuracy test: 0.66

所以效果很差。

我的期望:

我希望无论使用什么方法,神经网络都能检测到突破(可能是另一个神经网络或只是另一种方法)。 补充说明:我认为二元分类的想法是错误的。也许我应该检查的是每0.1s的数据是否有足够的整体下降趋势。

python machine-learning deep-learning pytorch neural-network
1个回答
-1
投票

我根据您提供的过滤图合成了一个数据集(10k 个样本),并训练了一个简单的序列到序列 LSTM 来预测序列中每个时间点的标签。我得到的验证准确度约为 84%。虽然不是很好,但比 66% 有了很大的进步。

这个答案只是第一个原型,展示了一个简单的架构,可以产生可行的初步结果。通过更多的数据和调整,我认为有更高的准确度的空间。

我主要使用的是:

  • 从过滤后的数据而不是原始数据开始。获得初始工作模型更清晰、更容易。
  • 简单的 LSTM 模型,提供窗口中每个时间点的预测
  • 基于每个时间步(序列到序列)的损失,而不仅仅是序列末尾的损失

这种方法提供了一个良好的起点。

综合数据:

输出:

[epoch  1]  train loss 0.668  | val accuracy 0.6378
[epoch  2]  train loss 0.558  | val accuracy 0.8347
[epoch  3]  train loss 0.462  | val accuracy 0.8295
[epoch  4]  train loss 0.439  | val accuracy 0.8363
[epoch  5]  train loss 0.425  | val accuracy 0.8429

导入和合成数据:

import pandas as pd
import numpy as np
from matplotlib import pyplot as plt

np.random.seed(0)

#
# Make a test dataset.
# You don't need this part because you have the actual filtered data.
#
df = pd.read_csv('../plot-data.csv')
df = df.set_axis(['x', 'y'], axis=1)

#Create a label vector, where 1 means drilling, 0: not drilling
label = np.where(df.y > 0.04, 1, 0)
label[0] = 0
label[[6, 13, 19, 26, 33, 40]] = 1
df['label'] = label

x_fine = np.linspace(df.x.min(), df.x.max(), num=2000)
y_fine = np.interp(x_fine, df.x, df.y)
label_fine = np.interp(x_fine, df.x, df.label)
label_fine = np.round(lab_fine)

#These represent the final data to be used next
time_all = np.linspace(x_fine[0], x_fine[-1] * 5, len(x_fine) * 5)
force_all = np.tile(y_fine, 5) + np.random.randn(x_all.shape[0]) / 500
label_all = np.tile(lab_fine, 5)

#View the data and labels
plt.scatter(time_all, force_all, marker='.', c=label_all, cmap='coolwarm', s=1)
plt.gcf().set_size_inches(11, 3)
plt.xlabel('x')
plt.ylabel('force')

创建一个

DrillDataset
类,并包装在数据加载器中以进行批处理:

import torch
from torch.utils.data import Dataset, DataLoader
from torch import optim
from torch import nn

#
# Prepare dataset
#

#Returns windows from the data and labels
# Windows have size sequence_length
# Window n will return a tuple of (force data from n:n+seq_length, labels n:n+seq_length)
#
class DrillDataset:
    def __init__(self, force_data, label_data, sequence_length):
        self.sequence_length = sequence_length

        #Data to float tensors
        self.force_data = torch.tensor(force_data).float()
        self.label_data = torch.tensor(label_data).float()
    
    def __len__(self):
        return len(self.force_data) - self.sequence_length
    
    def __getitem__(self, index):
        if index < 0:
            index += self.__len__()
            
        if index >= self.__len__():
            raise IndexError()
        
        window = slice(index, index + self.sequence_length)
        return self.force_data[window].reshape(-1, 1), self.label_data[window]

sequence_length = 20
batch_size = 32

#Train/validation split of 70%/30%
# For brevity I haven't created a test split
# You'll likely need a test split if you want a final unbiased assessemnt
# of accuracy.
train_samples = int( len(force_all) * 0.7 )
train_dataset = DrillDataset(force_all[:train_samples],
                             label_all[:train_samples],
                             sequence_length)

val_dataset = DrillDataset(force_all[train_samples:],
                           label_all[train_samples:],
                           sequence_length)

#Wrap in DataLoaders that batchify the dataset
train_loader = DataLoader(train_dataset, shuffle=True, batch_size=batch_size, num_workers=2)
val_loader = DataLoader(val_dataset, batch_size=len(val_dataset))

定义模型和训练:

#
# Define model
#

#This is a custom layer that let's me
# specify lambda functions.
# Useful for quick and simple manipulations like
# swapping dimenions. 
class LambdaLayer(nn.Module):
    def __init__(self, func):
        super().__init__()
        self.func = func
    
    def forward(self, x):
        return self.func(x)
    
model = nn.Sequential(
    #1. Permute from [batch, seq_len, 1] to [seq_len, batch, 1]
    # This is because in PyTorch sequential models are
    # batch_first=False by default.
    LambdaLayer(lambda x: x.swapdims(0, 1)),

    #2. One-layer LSTM that projects to a hidden size of 32, and then
    # projects back down to 1 (same as input dim) at the output
    nn.LSTM(input_size=1, hidden_size=32, num_layers=1, proj_size=1),

    #3. Get the output at each time step ("output") and ignore
    # the other tensors from the LSTM (h_n and c_n).
    # Also, permute from [seq_len, batch, 1] back to [batch, seq_len, 1]
    LambdaLayer(lambda output_hncn: output_hncn[0].swapdims(0, 1)),

    #4. For the output at each time step, provide a value
    # between 0 (no drilling) and 1 (drilling). Done using a sigmoid layer.
    nn.Sigmoid(),
)

#Try Adam optimiser initially. Tends to work well.
optimiser = optim.Adam(model.parameters())

n_epochs = 5
for epoch in range(n_epochs):
    model.train()
    cum_loss = 0 #keep track of train loss over each epoch
    
    for X_mb, y_mb in train_loader: #For each minibatch X_mb, y_mb
        pred_proba = model(X_mb).squeeze()
        loss = nn.BCELoss()(pred_proba, y_mb)
        
        optimiser.zero_grad()
        loss.backward()
        optimiser.step()
        
        cum_loss += loss.item() * len(X_mb)
    
    #End of epoch
    model.eval()
    
    #Get the validation accuracy
    with torch.no_grad():
        val_x, val_y = next(iter(val_loader))
        val_acc = (model(val_x).squeeze().round() == val_y).float().mean()
    
    #Print train loss and val accuracy
    print(
        f'[epoch {epoch + 1:>2d}] ',
        f'train loss {cum_loss / len(train_loader.dataset):>5.3f} ',
        f'| val accuracy {val_acc:>.4f}'
    )
© www.soinside.com 2019 - 2024. All rights reserved.