주간 다이어리 - 6주차
활동 기록
팀 활동
- 4월 8일(월) 21:00 ~ (정기 미팅)
→ 총 1시간 00분 진행
개별 활동
- 유재휘
- 2024.04.10 : Mel spectrum 알고리즘 보완 - 약 4시간 30분
-
2024.04.11 : Mel spectrum 알고리즘 수정, 데이터셋에 Mel 적용 & 저장 - 약 2시간
→ 총 6시간 30분 진행
- 전준표
- 2024.04.09 : 추가 구매 물품 조사 및 신청
- 2024.04.14 : Siamese networks 모델 구현
→ 총 7시간 진행
- 이민석
-
2024.04.14 : 하드웨어 프레임 세부 설계 - 약 2시간
→ 총 2시간 진행
-
- 조민수
- 2024.04.09 : 프로그램 UI 코드 보완 및 git 업로드 - 2시간(10:00~12:00)
-
2024.04.14 : 선택지 단어 및 문장 추가 + 해당 이미지 화면 연결 코드 수정 - 약 3시간 30분(01:00-04:30)
→ 총 5시간 30분 진행
진행 상황
1) 하드웨어 설계 수정
- 마이크의 크기가 예상보다 컸던 점, 기존 하드웨어 설계로는 마이크의 수납이 불가능하다는 점으로부터 수납이 가능한 형태로 하드웨어의 설계를 수정하였다.
- 아래의 층은 수납공간으로 활용할 예정이며 마이크 및 음성데이터 수집에 필요한 부속 부품의 수납공간으로 활용할 예정이며 위 층에는 라즈베리파이 및 터치 디스플레이, 스피커 등을 설치할 예정이다.
- 세부적인 치수 조정은 다음과 같다
2) 데이터셋 전처리 알고리즘 보완
보완된 Mel spectrum 알고리즘 코드
# ----------------------------------------------------------------
#1
import os
import numpy as np
import librosa
import matplotlib.pyplot as plt
import wave
# import struct
# ----------------------------------------------------------------
# ----------------------------------------------------------------
#2
# .pcm -> .wav 변환하는 파트
def pcm_to_wav(pcm_file, wav_file, channels, sample_width, frame_rate):
# PCM 파일 열기
with open(pcm_file, 'rb') as pcm:
# PCM 파일의 오디오 데이터 길이 확인 -> 이 부분이 없으면 배속으로 저장됨
data = pcm.read()
pcm_length = len(data)
# 오디오의 샘플 수 계산
num_samples = pcm_length // (channels * sample_width)
# WAV 파일 열기
with wave.open(wav_file, 'wb') as wav:
# WAV 파일 헤더 설정
wav.setnchannels(channels)
wav.setsampwidth(sample_width)
wav.setframerate(frame_rate)
wav.setnframes(num_samples)
# 오디오 데이터 쓰기
wav.writeframes(data)
# ----------------------------------------------------------------
# ----------------------------------------------------------------
#3
# 현재 위치한 디렉토리 내의 확장자가 .pcm인 모든 파일 불러오기
# 경로 필요할때마다 수정하기 (현재는 코드파일과 같은 곳에 위치한 것 사용)
pcm_directory = "."
my_pcm_data = [f for f in os.listdir('.') if f.endswith(".pcm")]
# 반복하여 한번에 여러 파일 처리
for pcm_file in my_pcm_data:
# .pcm 파일의 경로
pcm_sound = os.path.join(pcm_directory, pcm_file)
# .wav 파일의 경로
wav_file = os.path.join(pcm_directory, os.path.splitext(pcm_file)[0] + ".wav")
pcm_to_wav_sound = os.path.join(pcm_directory, wav_file)
# PCM을 WAV로 변환
pcm_to_wav(pcm_sound, pcm_to_wav_sound, channels=1, sample_width=2, frame_rate=16000)
# WAV 파일 읽기
y, sr = librosa.load(pcm_to_wav_sound)
# Mel-spectrogram 계산
mel_spectrogram = librosa.feature.melspectrogram(y=y, sr=sr)
# Mel-spectrogram을 데시벨로 변환
mel_spectrogram_db = librosa.power_to_db(mel_spectrogram, ref=np.max)
# Mel-spectrogram 플로팅
plt.figure(figsize=(10, 4))
axes = librosa.display.specshow(mel_spectrogram_db, sr=sr, x_axis='time', y_axis='mel')
# plot 그래프 이미지만 나타내기
plt.yticks(ticks= []) # y축 tick 제거
plt.xticks(ticks= []) # x축 tick 제거
# # x축, y축 각각 눈금 & 테두리 제거
plt.gca().axes.get_xaxis().set_visible(False)
plt.gca().axes.get_yaxis().set_visible(False)
plt.gca().spines['bottom'].set_visible(False)
plt.gca().spines['left'].set_visible(False)
plt.gca().spines['top'].set_visible(False)
plt.gca().spines['right'].set_visible(False)
# 제목과 컬러바(오른쪽 색상표시바) 제거
plt.title('')
plt.colorbar().remove()
# 이미지 저장 (그래프 내용 부분만...흰 배경X)
output_filename = os.path.join(pcm_directory, f"Mel_spectrum_{os.path.splitext(os.path.basename(pcm_to_wav_sound))[0]}.png")
plt.axis('off')
plt.savefig(output_filename, bbox_inches='tight', pad_inches=0)
plt.close()
# ----------------------------------------------------------------
Mel 알고리즘 코드 기능
- .pcm 확장자 음원파일 → .wav 확장자 음원파일로 변환
- 변환된 .wav 음원파일에 Mel spectrum 알고리즘 적용
- 만들어진 plot 이미지를 같은 디렉토리 내에 저장
- 이름 형식 : Mel_spectrum_[원본 .wav 파일 이름].png
- 저장되는 plot 이미지에서 그래프 파형 부분만 잘라 저장 (색상 표시바, x축, y축 등 제거)
Mel 알고리즘 적용 결과 이미지
Mel 이미지1 - plot 전체 버전
Mel 이미지2 - plot 파형만 자른 버전
3) Siamese networks 구현
- Siamese networks github와 각종 자료를 참고하여 Siamese networks 모델에 대한 Python 코드를 작성
- 필요한 모듈 import
%matplotlib inline
import torchvision
import torchvision.datasets as dset
import torchvision.transforms as transforms
from torch.utils.data import DataLoader,Dataset
import matplotlib.pyplot as plt
import torchvision.utils
import numpy as np
import random
from PIL import Image
import torch
from torch.autograd import Variable
import PIL.ImageOps
import torch.nn as nn
from torch import optim
import torch.nn.functional as F
- 학습 결과 표시를 위한 함수 및 configuration 클래스 정의
def imshow(img,text=None,should_save=False):
npimg = img.numpy()
plt.axis("off")
if text:
plt.text(75, 8, text, style='italic',fontweight='bold',
bbox={'facecolor':'white', 'alpha':0.8, 'pad':10})
plt.imshow(np.transpose(npimg, (1, 2, 0)))
plt.show()
def show_plot(iteration,loss):
plt.plot(iteration,loss)
plt.show()
class Config():
training_dir = "/content/training"
testing_dir = "/content/testing"
train_batch_size = 64
train_number_epochs = 100
- 데이터 로딩 클래스 정의
class SiameseNetworkDataset(Dataset):
def __init__(self,imageFolderDataset,transform=None,should_invert=False):
self.imageFolderDataset = imageFolderDataset
self.transform = transform
self.should_invert = should_invert
def __getitem__(self,index):
img0_tuple = random.choice(self.imageFolderDataset.imgs)
should_get_same_class = random.randint(0,1)
if should_get_same_class:
while True:
img1_tuple = random.choice(self.imageFolderDataset.imgs)
if img0_tuple[1]==img1_tuple[1]:
break
else:
while True:
img1_tuple = random.choice(self.imageFolderDataset.imgs)
if img0_tuple[1] !=img1_tuple[1]:
break
img0 = Image.open(img0_tuple[0])
img1 = Image.open(img1_tuple[0])
img0 = img0.convert("L")
img1 = img1.convert("L")
if self.should_invert:
img0 = PIL.ImageOps.invert(img0)
img1 = PIL.ImageOps.invert(img1)
if self.transform is not None:
img0 = self.transform(img0)
img1 = self.transform(img1)
return img0, img1 , torch.from_numpy(np.array([int(img1_tuple[1]!=img0_tuple[1])],dtype=np.float32))
def __len__(self):
return len(self.imageFolderDataset.imgs)
- Siamese Network 정의
class SiameseNetwork(nn.Module):
def __init__(self):
super(SiameseNetwork, self).__init__()
self.cnn1 = nn.Sequential(
nn.ReflectionPad2d(1),
nn.Conv2d(1, 4, kernel_size=3),
nn.ReLU(inplace=True),
nn.BatchNorm2d(4),
nn.ReflectionPad2d(1),
nn.Conv2d(4, 8, kernel_size=3),
nn.ReLU(inplace=True),
nn.BatchNorm2d(8),
nn.ReflectionPad2d(1),
nn.Conv2d(8, 8, kernel_size=3),
nn.ReLU(inplace=True),
nn.BatchNorm2d(8),
)
self.fc1 = nn.Sequential(
nn.Linear(8*100*100, 500),
nn.ReLU(inplace=True),
nn.Linear(500, 500),
nn.ReLU(inplace=True),
nn.Linear(500, 5))
def forward_once(self, x):
output = self.cnn1(x)
output = output.view(output.size()[0], -1)
output = self.fc1(output)
return output
def forward(self, input1, input2):
output1 = self.forward_once(input1)
output2 = self.forward_once(input2)
return output1, output2
- Contrastive Loss 계산
class ContrastiveLoss(torch.nn.Module):
def __init__(self, margin=2.0):
super(ContrastiveLoss, self).__init__()
self.margin = margin
def forward(self, output1, output2, label):
euclidean_distance = F.pairwise_distance(output1, output2, keepdim = True)
loss_contrastive = torch.mean((1-label) * torch.pow(euclidean_distance, 2) +
(label) * torch.pow(torch.clamp(self.margin - euclidean_distance, min=0.0), 2))
return loss_contrastive
- 학습 코드 작성
folder_dataset = dset.ImageFolder(root=Config.training_dir)
siamese_dataset = SiameseNetworkDataset(imageFolderDataset=folder_dataset,
transform=transforms.Compose([transforms.Resize((100,100)),
transforms.ToTensor()
])
,should_invert=False)
train_dataloader = DataLoader(siamese_dataset,
shuffle=True,
num_workers=8,
batch_size=Config.train_batch_size)
net = SiameseNetwork().cuda()
criterion = ContrastiveLoss()
optimizer = optim.Adam(net.parameters(),lr = 0.0005 )
counter = []
loss_history = []
iteration_number= 0
for epoch in range(0,Config.train_number_epochs):
for i, data in enumerate(train_dataloader,0):
img0, img1 , label = data
img0, img1 , label = img0.cuda(), img1.cuda() , label.cuda()
optimizer.zero_grad()
output1,output2 = net(img0,img1)
loss_contrastive = criterion(output1,output2,label)
loss_contrastive.backward()
optimizer.step()
if i %10 == 0 :
print("Epoch number {}\n Current loss {}\n".format(epoch,loss_contrastive.item()))
iteration_number +=10
counter.append(iteration_number)
loss_history.append(loss_contrastive.item())
show_plot(counter,loss_history)
4) 프로그램 작동 코드
- 기존 작업했던 코드에 관해서 깃허브에 업로드
-
단어 자체적으로 5가지 만들기 및 해당 단어 선택하여 학습 시도 하도록 ui 제작
- 버튼 클릭시 다음 화면으로 넘어가는 코드 제작
- 필요한 모듈 import
import os
import sys
from PyQt5.QtWidgets import *
from PyQt5 import uic
- 각 화면을 나타내는 ui 윈도우에 나타나도록 하는 코드
class WindowClass(QMainWindow, form_class):
def __init__(self):
super().__init__()
self.setupUi(self)
- 현재 화면에서 버튼 클릭 시 이벤트 발생 함수 제작
def btn_main_to_second(self):
self.hide()
self.second = check_noisewindow()
self.second.exec()
self.show()
- 뒤로가기 버튼을 클릭하였을 경우 이전화면으로 돌아가는 함수
def btn_second_to_main(self):
self.close()
- 전체적인 함수 실행 코드
if __name__ == '__main__':
app = QApplication(sys.argv)
myWindow = WindowClass()
myWindow.show()
app.exec_()