6 분 소요

활동 기록


팀 활동

  • 5월 7일(화) 10:00 ~ 10:35 (오프라인 미팅)
  • 5월 7일(화) 10:40 ~ 11:30 (오프라인 개발)
  • 5월 7일(화) 13:00 ~16:00 (오프라인 개발)
  • 5월 8일(수) 10:00 ~ 11:30 (오프라인 개발 및 중간 발표자료 제작)
  • 5월 8일(수) 13:00 ~ 16:10(오프라인 개발 및 중간 발표자료 제작)

→ 총 9시간 5분 진행

개별 활동

  • 전준표
    • 5월 6일(월) 20:20 ~ 21:30(Siamese network 학습)
    • 5월 6일(월) 23:30 ~ 24:00(Siamese network 학습결과 분석)
    • 5월 9일(목) 13:30 ~ 15:00 (중간 발표자료 수정)

    → 총 3시간 10분 진행

  • 유재휘
    • 5월 7일(화) 10:00 ~ 12:00 (TTS 알고리즘 최종 수정 완료)
    • 5월 7일(화) ~ 5월 8일(수) - 중간발표 PPT 제작
  • 조민수
    • 5월 7일(화) ~ 5월 8일(수)(Siamese network 메인프로그램 코드내 작동)
      • (UI 화면 이미지 추가하여 수정작업)

진행 상황


1) Siamese network 학습

  • 학습 결과를 확인할 때, 유사도가 낮았지만 클래스가 동일하게 나타나는 결과가 존재하였음. → 해당 원인을 분석하기 위해서는 원본 데이터셋(음성파일)의 확인이 불가피하다 → 기존에 가공된 음성 데이터셋은 원본확인을 고려하지 않았기 때문에 원본 확인을 고려하여 데이터 셋을 다시 제작하여 학습을 다시 진행하였다
  • 데이터 셋 수정사항
    • 전 처리 과정에서 VAD(음성의 공백부분 제거) 처리 추가
    • mel-spectrogram 과 원본 음성데이터간의 mapping 작업 (학습 결과 확인을 위함)
  • 3차 학습
    • training 데이터
      • 한국인 발화 음성 90장
      • 영어권, 아시아권 외국인 발화 음성 각 45장 총 90장
    • testing 데이터
      • 한국인 발화 음성 10장
      • 영어권, 아시아권 외국인 발화 음성 각 5장 총 10장
    • batch_size : 64, epoch : 100
    • contrastiveLoss margin : 2.0
      • contrastiveLoss margin : 클래스가 다른 두 샘플 간의 최소한의 거리 값. 두 샘플간의 거리가 margin 이상이 되도록 학습
  • 3차 학습 결과
    • loss : 0.003

    • 정확도

    threshold 1.0 → 97.7%

    threshold 0.5 → 89.25%

    • 3차 학습 결과 피드백
      • 손실 함수의 margin값을 1.0에서 2.0으로 변경 및 VAD를 적용한 결과, 정확도가 20퍼 가량 상승하였다. 육안으로 보았을 때도 한국인 음성과 외국인 음성의 구분을 잘 해주는 것으로 보인다.
      • 유사도의 정도를 파악하기 위하여 결과들의 실제 음성 데이터를 확인할 필요가 있어보임. 이를 통하여 최종적으로는 margin값의 미 조정이 필요

2) Google Cloud TTS API 수정 (남/녀 목소리 변경)

수정하는 코드 부분은 아래와 같음…name 부분이 사용할 음성의 이름 (구분해주는 것)

기존 TTS API 코드는 language_code 부분만 “ko-KR”로 존재했어서 원하는 성별의 목소리를 선택하지 못하였음 → name 부분을 추가하여 남/녀 필요한 목소리로 TTS 변환 후 출력

Google Cloud TTS API 한국어 지원 음성 모음 (표준)

국가 language_code name 성별
한국어(대한민국) ko-KR ko-KR-Standard-A 여성 (높은 톤)
한국어(대한민국) ko-KR ko-KR-Standard-B 여성 (낮은 톤)
한국어(대한민국) ko-KR ko-KR-Standard-C 남성 (낮은 톤)
한국어(대한민국) ko-KR ko-KR-Standard-D 남성 (높은 톤)

국적/성별에 따른 지원 음성 모음 링크

*: [지원되는 음성 및 언어     Cloud Text-to-Speech API     Google Cloud](https://cloud.google.com/text-to-speech/docs/voices?hl=ko)*

목소리 예시 정리

ko-KR-Standard-A.wav

ko-KR-Standard-A.wav

ko-KR-Standard-B.wav

ko-KR-Standard-B.wav

ko-KR-Standard-C.wav

ko-KR-Standard-C.wav

ko-KR-Standard-D.wav

ko-KR-Standard-D.wav

startTTS_woman.py (여성 높은 톤 버전) → 기존에 사용했던 여성 음성

# ----------------------------------------------------------------
from google.cloud import texttospeech
from google.oauth2 import service_account
# ----------------------------------------------------------------

# ----------------------------------------------------------------
# TTS 변환 기능 함수
def run_tts():

    credentials = service_account.Credentials.from_service_account_file('[key이름].json')

    client = texttospeech.TextToSpeechClient(credentials=credentials)
    
    text_block = input("텍스트 입력 : ")
    
    synthesis_input = texttospeech.SynthesisInput(text=text_block)

    voice = texttospeech.VoiceSelectionParams(
        language_code="ko-KR",
        name="ko-KR-Standard-A",
        ssml_gender=texttospeech.SsmlVoiceGender.NEUTRAL
    )

    audio_config = texttospeech.AudioConfig(
        audio_encoding=texttospeech.AudioEncoding.MP3
    )

    response = client.synthesize_speech(
        input=synthesis_input, voice=voice, audio_config=audio_config
    )
    
    OUTPUT = input("저장할 파일이름 & 확장자 입력 : ")
    
    with open(OUTPUT, "wb") as out:
        out.write(response.audio_content)
        print(f'Audio content written to file "{OUTPUT}"')
# ----------------------------------------------------------------

# ----------------------------------------------------------------
# main문에서 run_tts 함수 실행
if __name__ == "__main__":
    run_tts()
# ----------------------------------------------------------------

startTTS_man.py (남성 낮은 톤 버전)

# ----------------------------------------------------------------
from google.cloud import texttospeech
from google.oauth2 import service_account
# ----------------------------------------------------------------

# ----------------------------------------------------------------
# TTS 변환 기능 함수
def run_tts():

    credentials = service_account.Credentials.from_service_account_file('[key이름].json')

    client = texttospeech.TextToSpeechClient(credentials=credentials)
    
    text_block = input("텍스트 입력 : ")
    
    synthesis_input = texttospeech.SynthesisInput(text=text_block)

    voice = texttospeech.VoiceSelectionParams(
        language_code="ko-KR",
        name="ko-KR-Standard-C",
        ssml_gender=texttospeech.SsmlVoiceGender.NEUTRAL
    )

    audio_config = texttospeech.AudioConfig(
        audio_encoding=texttospeech.AudioEncoding.MP3
    )

    response = client.synthesize_speech(
        input=synthesis_input, voice=voice, audio_config=audio_config
    )
    
    OUTPUT = input("저장할 파일이름 & 확장자 입력 : ")
    
    with open(OUTPUT, "wb") as out:
        out.write(response.audio_content)
        print(f'Audio content written to file "{OUTPUT}"')
# ----------------------------------------------------------------

# ----------------------------------------------------------------
# main문에서 run_tts 함수 실행
if __name__ == "__main__":
    run_tts()
# ----------------------------------------------------------------

+

“남성 tts 목소리 mel 이미지”와 “여성 tts 목소리 mel 이미지” 비교

변환한 텍스트 내용

: main코드를 이용해 TTS, VAD, MEL 알고리즘 테스트 중입니다. [성별]목소리입니다.

man_after_VAD_record.wav

man_after_VAD_record.wav

woman_after_VAD_record.wav

woman_after_VAD_record.wav

man_Mel_after_VAD_record.jpg

woman_Mel_after_VAD_record.jpg

3) Siamese network 동작 테스트

colab 에서 학습시킨 모델이 라즈베리파이와 메인 프로그램을 개발중인 PC에서 정상적으로 동작 하는지 확인하기 위하여 Test 코드를 작성하였다.

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

class Config():
    testing_dir = "/content/drive/MyDrive/testing" 

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 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])
        

        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))
        # 두 이미지가 서로 다른 클래스면 1
        # 두 이미지가 서로 같은 클래스면 0

    def __len__(self):
        return len(self.imageFolderDataset.imgs)

class SiameseNetwork(nn.Module):
    def __init__(self):
        super(SiameseNetwork, self).__init__()
        self.cnn1 = nn.Sequential(
            nn.ReflectionPad2d(1), # 가장자리의 특징들까지 고려
            nn.Conv2d(3, 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*99*250, 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) # flatten
        output = self.fc1(output)
        return output

    def forward(self, input1, input2):
        output1 = self.forward_once(input1)
        output2 = self.forward_once(input2)
        return output1, output2

device = "cuda" if torch.cuda.is_available() else "cpu"
model = torch.load("siamese_net_v4.pt", map_location=device)
print(model)

folder_dataset_test = dset.ImageFolder(root=Config.testing_dir)
siamese_dataset = SiameseNetworkDataset(imageFolderDataset=folder_dataset_test,
                                        transform=transforms.Compose([transforms.Resize((99,250)),
                                                                      transforms.ToTensor()
                                                                      ])
                                       ,should_invert=False)

test_dataloader = DataLoader(siamese_dataset,num_workers=1,batch_size=1,shuffle=True) # 1장씩 test

dataiter = iter(test_dataloader)
# x0,x1,label1 = next(dataiter) # test의 기준이 되는 img.
# 0 : same class , 1 : other class

for i in range(10):
    x0,x1,label1 = next(dataiter)
    concatenated = torch.cat((x0,x1),0)

    # output1,output2 = model(Variable(x0).cuda(),Variable(x1).cuda())
    output1,output2 = model(Variable(x0),Variable(x1))
    euclidean_distance = F.pairwise_distance(output1, output2)
    imshow(torchvision.utils.make_grid(concatenated),'isNotSame : {:.0f}\nDissimilarity: {:.2f}'.format(label1.item(),euclidean_distance.item()))
  • test 코드를 실행시키기 위해 필요한 환경
    • 모듈
      • python : 3.10 이상
      • pytorch, torchvision : 2.3.0
      • matplotlib : 3.7.1
    • 파일
      • test dataset ( KR 10장, ENG 5장, JP/CN 5장)
      • train된 Siamese Network pt 파일
  • 결과
    • 개발 pc 및 라즈베리파이에서 모델추론이 정상적으로 이루어지는 것을 확인

  • 발생한 문제 1
    • colab 에서 사용했던 test 코드를 그대로 가져와서 사용하였을 때 실행도중에 에러가 발생
    • 원인
      • 라즈베리파이는 gpu가 존재하지 않기 때문에 추론 준비과정에서 데이터들을 gpu로 올리는 코드에서 문제가 발생

        → 데이터를 cpu상에서 처리하도록 코드를 변경하여 해당 문제 해결

  • 발생한 문제 2
    • 에러1
      • 에러 문구

          Can't get attribute 'SiameseNetwork' on <module '__main__' (built-in)>
        
      • 메인프로그램에서 동작 테스트 → 모듈이 일치하지 않다는 에러 → 기존 모델 학습과 모듈이 달라서 생긴 에러 → 모듈 버전 재설치 후 실행 → 해당 에러 해결

    • 에러2
      • 에러 문구

          raise RuntimeError(f'DataLoader worker (pid(s) {pids_str}) exited unexpectedly') from e
          RuntimeError: DataLoader worker (pid(s) 11120) exited unexpectedly
        
      • 프로그램에서 동작 테스트 → 프로그램 상 메모리(cpu 혹은 gpu) 부족함 →

      num_workers= 1→0 로 수정 → 동작 확인

    • 결과
      • 메인 프로그램에서 정상적으로 잘 작동하는 것을 확인

4) UI 파일

기존 버튼에 이미지 파일 추가

flaticon에 존재하는 벡터 이미지를 사용해서 리소스파일을 qtdesigner에 추가.

원하는 버튼의 stylesheet를 수정 작업

border-image:url(해당 파일 이미지 경로)

해당 코드를 추가하여 버튼에 이미지를 추가

파이썬 코드에서 해당 리소스 파일을 사용 결과

No module named 'button_rc'

에러 발생 이유 → 해당리소스 파일의 qrc 파일은 파이썬에서 불러올 수 없음

→ 해결방안 : qrc 파일을 py파일로 수정해주는 작업을 추가 및 해당 py파일을 import하면 사용 가능

pyrcc5 button.qrc -o button_rc.py(qrc파일을 py파일로 수정해주는 작업)

import button_rc.py ->해당 리소스 파일 import

→ 실행 결과 : 해당 프로그램이 잘 작동하는 것을 확인 할 수 있음

5) HW 외관 제작

  • HW 외관 예시

HW외관은 라즈베리파이를 제외한 부품들은 안보이게 할 예정이며, 뒤에는 사운드센서가 소음을 감지할 수 있도록 하는 구멍 + 라즈베리파이 전원 연결을 위한 구멍을 뚫어서 사용할 예정이다

  • HW 외관 실제 제작

네모 아크릴 박스위에 검은색 시트지를 붙여 라즈베리파이를 제외한 다른 부품들이 보여지않도록 하였으며, 뒤에 구멍까지 뚫어 외관은 완성한 상태

6) 중간발표 자료제작

금주 금요일(5/10)에 예정되어있는 캡스톤 디자인 중간 발표회를 위하여 발표 자료 제작

(’캔바’ ppt & 플로우차트 템플릿 이용)

5분 내로 발표를 마쳐야 하기 때문에 최대한 간결하게 핵심만 정리하고, 추후 질의응답 시간 때 구체적인 설명을 할 것이다. 이러한 이유로 만든 발표자료의 목차는 다음과 같다.

  • 목차
    • 작품 개요
    • 핵심 기능
    • 플로우차트
    • H/W & S/W 진행상황
    • 각자 맡은 역할

비고