Deep Learning

오디오 딥러닝을 해봅시다! (Sound Classification) - 2. 모델을 이용해서 학습하기

jinmc 2022. 9. 8. 11:03
반응형

전 포스팅 데이터 전처리 에서 데이터 전처리 과정에 대해서 다뤄봤습니다.

이번 포스팅에서는 데이터 전처리에서 처리한 SoundDS 클래스를 어떻게 사용하는지 살펴봅시다.

 

Data Loader 를 사용한 데이터 Batch 사용하기

custom Dataset을 사용하여 Feature와 Label을 모은 데이터를 8:2로 나눕니다.

다음 이미지를 참조하여 주세요!

 

 

from torch.utils.data import random_split

myds = SoundDS(df, data_path)

# Random split of 80:20 between training and validation
num_items = len(myds)
num_train = round(num_items * 0.8)
num_val = num_items - num_train
train_ds, val_ds = random_split(myds, [num_train, num_val])

# Create training and validation data loaders
train_dl = torch.utils.data.DataLoader(train_ds, batch_size=16, shuffle=True)
val_dl = torch.utils.data.DataLoader(val_ds, batch_size=16, shuffle=False)

Training data에서 random하게 하나의 batch를 뽑아서 위와 같이 transform을 진행합니다.

몇 가지 프로세스를 같이 살펴봅시다.

 

1. audio file 은 numpy array로 들어가고, shape은 (num_channels, num_samples)가 됩니다.

만약 16000의 sample rate, 4초로 설정했다고 하면, 16000 * 4 = 64000개의 샘플이 나옵니다. 1 channel이라면,

(1, 64000)의 shape을 가지게 됩니다.

2. channel 과 sample rate이 다를 경우를 대비해서 transform이 진행됩니다.

3. 4초보다 적거나 많을 때에 대비해서 4초로 맞춰줍니다.

4. Time Shift function을 통해서 왼쪽이나 오른쪽으로 옮길수 있습니다. shape는 변하지 않습니다.

5. audio를 mel spectrogram으로 변환합니다. shape는 (num_channels, Mel freq_bands, time_steps) = (1, 64, 344)가 됩니다.

 

 

 

모델 만들기

pytorch를 이용해서 모델을 만듭니다.

모든 디테일을 다 언급하기는 힘들겠지만,

self.conv1 = nn.Conv2d(2, 8, kernel_size=(5, 5), stride=(2, 2), padding=(2, 2))

에서 앞에 2가 input이기 때문에, channel 수에 맞춰 줘야 됩니다.

또한, batch마다의 inference를 진행하기 때문에, 다음과 같은 특징들이 있습니다.

 

1. 한 batch마다의 shape은 다음과 같습니다. (batch_size, num_channels, Mel freq_bands, time_steps) 지금의 경우에는

(16, 2, 64, 344) 입니다.

2. CNN layer를 4개 지나는 동안 stride가 2이기 때문에, 반씩 줄어들게 되고, output channel은 64가 됩니다.

결국 마지막 shape은 (16, 64, 4, 22) 가 됩니다.

3. (16, 64)의 shape가 linear layer에 들어가게 됩니다.

4. 마지막 prediction을 위해서 class 10개로 들어가게 됩니다. 이는 조정이 가능한 것으로 보입니다.

 

CNN에 관한 얘기는 많이 하지 않겠지만, 워낙 얘기할 내용이 많아서.. 

여기서 사용된 모델이 꼭 최선의 모델이 아니라는 점만 얘기하고 싶네요!


import torch.nn.functional as F
from torch.nn import init

# ----------------------------
# Audio Classification Model
# ----------------------------
class AudioClassifier (nn.Module):
    # ----------------------------
    # Build the model architecture
    # ----------------------------
    def __init__(self):
        super().__init__()
        conv_layers = []

        # First Convolution Block with Relu and Batch Norm. Use Kaiming Initialization
        self.conv1 = nn.Conv2d(2, 8, kernel_size=(5, 5), stride=(2, 2), padding=(2, 2))
        self.relu1 = nn.ReLU()
        self.bn1 = nn.BatchNorm2d(8)
        init.kaiming_normal_(self.conv1.weight, a=0.1)
        self.conv1.bias.data.zero_()
        conv_layers += [self.conv1, self.relu1, self.bn1]

        # Second Convolution Block
        self.conv2 = nn.Conv2d(8, 16, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
        self.relu2 = nn.ReLU()
        self.bn2 = nn.BatchNorm2d(16)
        init.kaiming_normal_(self.conv2.weight, a=0.1)
        self.conv2.bias.data.zero_()
        conv_layers += [self.conv2, self.relu2, self.bn2]

        # Second Convolution Block
        self.conv3 = nn.Conv2d(16, 32, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
        self.relu3 = nn.ReLU()
        self.bn3 = nn.BatchNorm2d(32)
        init.kaiming_normal_(self.conv3.weight, a=0.1)
        self.conv3.bias.data.zero_()
        conv_layers += [self.conv3, self.relu3, self.bn3]

        # Second Convolution Block
        self.conv4 = nn.Conv2d(32, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
        self.relu4 = nn.ReLU()
        self.bn4 = nn.BatchNorm2d(64)
        init.kaiming_normal_(self.conv4.weight, a=0.1)
        self.conv4.bias.data.zero_()
        conv_layers += [self.conv4, self.relu4, self.bn4]

        # Linear Classifier
        self.ap = nn.AdaptiveAvgPool2d(output_size=1)
        self.lin = nn.Linear(in_features=64, out_features=10)

        # Wrap the Convolutional Blocks
        self.conv = nn.Sequential(*conv_layers)
 
    # ----------------------------
    # Forward pass computations
    # ----------------------------
    def forward(self, x):
        # Run the convolutional blocks
        x = self.conv(x)

        # Adaptive pool and flatten for input to linear layer
        x = self.ap(x)
        x = x.view(x.shape[0], -1)

        # Linear layer
        x = self.lin(x)

        # Final output
        return x

# Create the model and put it on the GPU if available
myModel = AudioClassifier()
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
myModel = myModel.to(device)
# Check that it is on Cuda
next(myModel.parameters()).device

 

 

학습하기

학습에서는, optimzer, loss, scheduler 등을 정해주면 됩니다.

epoch 숫자는 더 높게 할 수 있습니다.

 

# ----------------------------
# Training Loop
# ----------------------------
def training(model, train_dl, num_epochs):
  # Loss Function, Optimizer and Scheduler
  criterion = nn.CrossEntropyLoss()
  optimizer = torch.optim.Adam(model.parameters(),lr=0.001)
  scheduler = torch.optim.lr_scheduler.OneCycleLR(optimizer, max_lr=0.001,
                                                steps_per_epoch=int(len(train_dl)),
                                                epochs=num_epochs,
                                                anneal_strategy='linear')

  # Repeat for each epoch
  for epoch in range(num_epochs):
    running_loss = 0.0
    correct_prediction = 0
    total_prediction = 0

    # Repeat for each batch in the training set
    for i, data in enumerate(train_dl):
        # Get the input features and target labels, and put them on the GPU
        inputs, labels = data[0].to(device), data[1].to(device)

        # Normalize the inputs
        inputs_m, inputs_s = inputs.mean(), inputs.std()
        inputs = (inputs - inputs_m) / inputs_s

        # Zero the parameter gradients
        optimizer.zero_grad()

        # forward + backward + optimize
        outputs = model(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        scheduler.step()

        # Keep stats for Loss and Accuracy
        running_loss += loss.item()

        # Get the predicted class with the highest score
        _, prediction = torch.max(outputs,1)
        # Count of predictions that matched the target label
        correct_prediction += (prediction == labels).sum().item()
        total_prediction += prediction.shape[0]

        #if i % 10 == 0:    # print every 10 mini-batches
        #    print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 10))
    
    # Print stats at the end of the epoch
    num_batches = len(train_dl)
    avg_loss = running_loss / num_batches
    acc = correct_prediction/total_prediction
    print(f'Epoch: {epoch}, Loss: {avg_loss:.2f}, Accuracy: {acc:.2f}')

  print('Finished Training')
  
num_epochs=2   # Just for demo, adjust this higher.
training(myModel, train_dl, num_epochs)

 

인퍼런스 하기

인퍼런스는 val_dl이라는 우리가 만들었던 DataLoader에서 가져왔습니다.

wav 파일을 바로 사용한 것이 아니라 DataLoader를 사용하였기 때문에, 

실제 wav 나 mp4 파일을 사용하는 것이 아닌, DataLoader에 패키징 되어 있는 파일들을 사용한다는 점 

유의해 주시면 좋을것 같습니다.

 

이를 실제 wav 파일이나 mp4 파일에 사용하려면, DataLoader가 아닌 다른 포맷으로 사용해야 될 것으로 생각됩니다.

 

# ----------------------------
# Inference
# ----------------------------
def inference (model, val_dl):
  correct_prediction = 0
  total_prediction = 0

  # Disable gradient updates
  with torch.no_grad():
    for data in val_dl:
      # Get the input features and target labels, and put them on the GPU
      inputs, labels = data[0].to(device), data[1].to(device)

      # Normalize the inputs
      inputs_m, inputs_s = inputs.mean(), inputs.std()
      inputs = (inputs - inputs_m) / inputs_s

      # Get predictions
      outputs = model(inputs)

      # Get the predicted class with the highest score
      _, prediction = torch.max(outputs,1)
      # Count of predictions that matched the target label
      correct_prediction += (prediction == labels).sum().item()
      total_prediction += prediction.shape[0]
    
  acc = correct_prediction/total_prediction
  print(f'Accuracy: {acc:.2f}, Total items: {total_prediction}')

# Run inference on trained model with the validation set
inference(myModel, val_dl)

 

 

참조 : Audio deep learning made simple

반응형