11 분 소요

활동 기록


팀 활동

  • 5월 21일(화) 10:00 ~ 19:45 (오프라인 개발)
  • 5월 22일(수) 10:00 ~ 19:55 (오프라인 개발)
  • 5월 23일(목) 12:00 ~ 21:50 (오프라인 개발)
  • 5월 24일(금) 18:00 ~ 20:40 (오프라인 개발)

→ 총 32시간 10분 진행

개별 활동

  • 전준표
    • 5월 20일(월) 10:00 ~18:35 유사도 추론 모델 정확도 향상
    • 5월 20일(월) 21:00 ~ 25:30 노이즈 필터 코드 구현 및 흑백 모델 실험
    • 5월 22일(수) 22:30 ~ 26:10 발음 특화형 데이터 셋 제작 & 유사도 추론 모델 학습

    → 16시간 45분 진행

  • 조민수
    • 5월 20일(월) 10:00 ~ 14:30 유사도 추론모델 정확도 향상

    → 4시간 30분 진행

  • 유재휘
    • 5월 20일(월) 10:00 ~ 17:00메인 프로그램 실행 속도 향상 시도

    → 7시간 진행

진행 상황


1) Siamese network 학습 ( 4차 )

모델의 정확도를 향상시키기 위한 방법으로써 가장 먼저 데이터셋의 크기를 늘려보기로 하였다. 구체적인 학습 내용과 결과는 다음과 같다. 3차와의 주요한 변화는 training 데이터셋의 개수가 180장에서 총 920장으로 변경 되었다는 점이다.

  • 변경 사항
    • 데이터 셋 개수를 대폭 증가
    • 생성된 기준 데이터, 녹음데이터를 사용하여 추론 테스트
    • 유사도 점수화 알고리즘 완성으로 인하여, 점수로 결과의 정확도 판단
  • 4차 학습
    • training 데이터
      • 한국인 발화 음성 460장
      • 영어권, 아시아권 외국인 발화 음성 각 230장 총 460장
    • testing 데이터
      • 한국인 발화 음성 40장
      • 영어권, 아시아권 외국인 발화 음성 각 20장 총 40장
    • batch_size : 64, epoch : 50
    • contrastiveLoss margin : 2.0
  • 4차 학습 결과
    • loss : 0.002 ( siamese_net_v5_50epoch.pt)

    • 4차 학습 결과 피드백
      • 테스트를 해본 결과, 3차 대비 더 정확하게 나온 case도 있었지만 overfitting 되어 오히려 정확도가 더 떨어지는 case도 존재하였다. 이는 단순히 데이터 셋의 개수와 정확도가 비례하지 않는다는 것을 의미하며, 따라서 다음 학습부터는 결과를 도출하고 이유를 찾는 방식의 학습보다 근거(혹은 추론)를 먼저 세우고 이를 검증하는 접근방식으로 학습을 진행할 것이다.
    • 문제 발견

      • 위의 결과는 비록 서로 다른 문장을 발음하는 문장이지만 발음의 정확도 자체는 한국인과 비슷할 정도로 정확하였다. 하지만 추론 결과 36점이라는 낮은 점수가 나왔다. 이는 발음에 비해 확연하게 낮은 수치에 속하기 때문에 원인을 파악하기 위하여 조사를 진행하였다.

    추론의 근거로 사용되는 mel-spectrogram은 크게 3가지의 정보를 갖고있다.

    1. 시간 : 이미지의 x축을 담당.
    2. 주파수 : 이미지의 y축을 담당. 매 시간별 음성 신호가 갖는 주파수의 값을 의미
    3. 강도 : 이미지의 명암을 담당. 신호의 강도(소리의 크기)를 의미

    위의 3가지 정보 중에서 사용자의 발음을 분석하는 데에 있어서 동일하게 통제를 해야하는 부분은 “강도”, 즉 음성의 크기이다. 이는 동일한 문장, 발음에 대해 소리의 크기가 다르다는 이유만으로 정확도가 변할 가능성이 있기 때문이다. 하지만 기존의 데이터셋은 “강도”정보를 추론에 이용하기때문에, 이로인해 정확도가 영향을 받을 가능성이 존재하는 것으로 보인다.

    → 이를 검증하기 위하여 데이터 셋을 흑백으로 변경하여 학습을 진행 해 볼 예정이다.

2) Siamese network 학습 ( 5차 )

4차 학습에서 내린 가정을 검증하기 위한 학습 phase로, 데이터 셋을 RGB에서 grayscale로 변경하는 부분외에는 모두 동일하게 설정하여 학습을 진행하였다.

  • 변경 사항
    • RGB grayscale로 데이터 셋 변경
    • grayscale의 학습에 대응하도록 모델 코드 변경
  • 5차 학습
    • training 데이터
      • 한국인 발화 음성 460장
      • 영어권, 아시아권 외국인 발화 음성 각 230장 총 460장
    • testing 데이터
      • 한국인 발화 음성 40장
      • 영어권, 아시아권 외국인 발화 음성 각 20장 총 40장
    • batch_size : 64, epoch : 50
    • contrastiveLoss margin : 2.0
  • 5차 학습 결과
    • loss

    • → 초기 loss값의 분포가 컬러에 비해 전체적으로 낮았다.

  • 테스트
    • 4차 학습에서 사용했던 예제를 이용하여 테스트를 진행하였다.

    → 36점에서 46점으로 10점가량 점수가 오른 것을 볼 수 있다. 아래는 time-amplitude domain의 그래프인데, Y축의 scale을 보면 실제로 두 음성데이터 간의 “강도”에 차이가 있는 것을 알 수 있다.

  • 점수가 내려간 경우 분석

    위의 사진들은 오히려 점수가 떨어진 경우이다. 이에 대해 전과 같이 time-amplitude domain으로 변경 시켜보면

    위의 경우와 동일하게 “강도”의 차이가 있다. 해당 case 또한 “강도”를 추론의 정확도를 정하는 factor에서 제외했을 경우에 생기는 변화라고 예측할 수 있다.

  • 흑백 모델이 추론에 있어서 “강도”에 영향을 받지 않는지에 대한 추가 실험을 진행하였다.

    실험 방법은 다음과 같다.

    1. 기준 문장 데이터를 생성 (”오늘은 삼겹살을 먹었어요”)
    2. 작은 목소리로 녹음
    3. 2번의 음성 데이터의 소리만 증폭
    4. 1번의 데이터와 2,3번의 데이터를 비교

    3번의 증폭 데이터를 만들기 위하여 time-amplitude domain에서 y축에 값을 더해주는 방식을 사용하였다. 자세한 코드는 아래와 같다.

      from pydub import AudioSegment
        
      def amplify_volume(input_file, output_file, amplification_factor):
          # 입력 파일 로드
          sound = AudioSegment.from_file(input_file)
        
          # 볼륨 증폭
          amplified_sound = sound + amplification_factor
        
          # 증폭된 볼륨을 가진 파일 저장
          amplified_sound.export(output_file, format="wav")
        
      # 입력 파일과 출력 파일 지정
      input_file = 'filtered_record.wav'
      output_file = '10x_filtered_record.wav'
        
      # 볼륨 증폭 계수 지정
      amplification_factor = 20  # 원하는 증폭 정도로 조정
        
      # 볼륨 증폭된 음성 데이터를 새로운 파일에 저장
      amplify_volume(input_file, output_file, amplification_factor)
    

  • 실험 결과 ( 흑백 모델)

일반

10배

→ 흑백 모델에서 음성의 크기와 상관없이 동일한 결과값이 나타났다. 또한 음성의 크기가 달라졌음에도 mel-spectrogram의 명암이 크게 변하지 않은 것으로 보아, 명암은 절대값이 아니라 상대값일 가능성이 높을 것이다.

아래의 추가 실험의 결과까지 더하여, 최종적으로 변동성이 많은 컬러 데이터가 아닌 흑백 데이터를 사용하는 것이 정확도 추론에 더 유용하다. 따라서 앞으로 모든 추론은 흑백을 데이터를 이용하여 진행할 예정이다.

소리크기만 다른 컬러

소리크기만 다른 흑백

→ 음성은 동일. 소리의 크기만 다른 두 데이터끼리의 유사도를 비교하였을 때의 결과. 컬러는 94점 흑백은 99점이라는 결과가 나왔다. 이는 컬러의 경우 소리의 크기에 의해 유사도가 바뀔 수 있다는 것을 의미한다.

기준 데이터와 기본소리 컬러

기준 데이터와 10배 소리 컬러

→ TTS기준 데이터와 녹음 데이터(좌), TTS기준 데이터와 소리의 크기를 키운 녹음데이터(우). 변한 것은 소리의 크기밖에 없지만 유사도 점수가 변하였다.

기준 데이터와 소리다른 흑백 2개

→ TTS기준 데이터와 흑백 녹음데이터, TTS기준 데이터와 소리의 크기를 키운 흑백 녹음데이터. 두 결과 모두 유사도의 큰 차이가 없었다.

3) 노이즈 필터 구현

녹음 테스트를 하던 중, 녹음 데이터 파형이 아래와 같이 음성이 없는 부분에 노이즈가 발생하여 VAD(앞 뒤 공백제거)가 원활하게 이루어지지 않으며, 이에 따라 mel-spectrogram에 노이즈가 껴서 추론이 제대로 이루어 지지 않는 문제가 발생하였다.

영상의 흐릿한 부분들이 노이즈로 인해 생성된 부분

이러한 문제를 해결하기 위하여 노이즈를 제거해주는 코드를 다음과 같이 구현하였다.

from pydub import AudioSegment
import noisereduce as nr
import numpy as np
import matplotlib.pyplot as plt

# 녹음된 파일 불러오기
audio = AudioSegment.from_wav("record.wav")

noise_sample_duration = 1000
noise_sample = audio[:noise_sample_duration]

# AudioSegment를 numpy 배열로 변환하는 함수
def audiosegment_to_numpy(audio_segment):
    samples = np.array(audio_segment.get_array_of_samples())
    if audio_segment.channels == 2:
        samples = samples.reshape((-1, 2))
    return samples

# 노이즈 샘플과 전체 오디오를 numpy 배열로 변환
noise_data = audiosegment_to_numpy(noise_sample)
data = audiosegment_to_numpy(audio)

# 오디오의 샘플레이트 가져오기
rate = audio.frame_rate

# 노이즈 감소 적용
reduced_noise = nr.reduce_noise(y=data, sr=rate, y_noise=noise_data)

# numpy 배열을 다시 AudioSegment로 변환하는 함수
def numpy_to_audiosegment(data, frame_rate, sample_width, channels):
    return AudioSegment(
        data.tobytes(), 
        frame_rate=frame_rate,
        sample_width=sample_width,
        channels=channels
    )

# 노이즈 감소된 numpy 배열을 다시 AudioSegment로 변환
reduced_noise_audio = numpy_to_audiosegment(
    reduced_noise, 
    frame_rate=rate,
    sample_width=data.dtype.itemsize, 
    channels=audio.channels
)

# 필터링된 오디오 저장
reduced_noise_audio.export("filtered_record.wav", format="wav")

# 필터링된 오디오 재생 (필요 시)
# play(reduced_noise_audio)

# 필터링된 오디오를 시각화하여 확인
plt.plot(reduced_noise)
plt.title("filtered_record.wav")
plt.show()

노이즈 필터를 거친 녹음 데이터의 파형과, 이에 VAD를 적용시키고 mel-spectrogram으로 변환시킨 결과가 아래와 같다.

→ 파형과 mel-spectrogram 모두 노이즈가 제대로 제거되어 정상적으로 출력되는 모습이다.

녹음 데이터 뿐만 아니라 평가 기준 데이터(TTS)에도 노이즈 필터를 적용시켜 노이즈 제거를 진행하였고 파형 및 mel-spectrogram변화, 정확도 결과는 아래와 같다.

(노이즈 필터는 원래 VAD 전처리 전에 적용시키지만, TTS 데이터의 경우 에러가 발생하여 VAD처리 후에 필터를 입혔다.)

필터 전

필터 후

TTS 필터 전

TTS 필터 후

(맨 위부터 3차학습, 4차학습(epoch100), 4차학습(epoch50))

→ 두 데이터에 노이즈 필터를 씌우고 추론을 진행하니 전체적으로 정확도가 상승하는 결과를 얻을 수 있었다.

4) 시간축 정규화 시도

순서 : filtering → VAD → 정규화? → mel

5) 메인 프로그램 실행속도 향상 시도 (실패)

시도한 방법

  1. “numba” 모듈 사용
  2. 전역변수(global) 사용 중지
  3. Cpython 변형
1. numba 모듈

파이썬의 실행 속도를 개선하기 위한 라이브러리

  • JIT(just-in-time)라는 컴파일러 사용

    ex) 사용 예

      @jit
      @jit(nopython=True) # 옵션 사용할 때...
    
  • 일부 파이썬 코드와 Numpy에 대해서만 작동, 다른 모듈(pandas 등…)을 이용한 코드는 최적화X

numba 테스트코드 (numba 사용X)

import random
import time
import math

def some_function(n):
    z = 0
    for i in range(n):
        x = random.random()
        y = random.random()
        z += math.sqrt(x ** 2 + y ** 2)
    return z

start = time.time()

some_function(10000000)

end = time.time()
print(end - start)

numba 테스트코드 (numba 사용O)

import random
import time
import math

from numba import jit

@jit(nopython=True)  # (nopython=True) 옵션 지워도 같은 결과...
def some_function(n):
    z = 0
    for i in range(n):
        x = random.random()
        y = random.random()
        z += math.sqrt(x ** 2 + y ** 2)
    return z

start = time.time()

some_function(10000000)

end = time.time()
print(end - start)

테스트 결과

numba 사용X일 때의 실행속도

아래

numba 사용O일 때의 실행 속도

파이썬 테스트코드 실행속도 비교

실패 이유

1) “numba” 모듈은 numpy 등의 파이썬 기본 코드만 지원하며, pythorch 같은 외부 모듈은 코드를 이해하지 못하여 최적화 불가능

2) numpy 형식으로 코드를 바꾸려다가 더 느려질 수 있음

3)

2. 전역변수(global) 사용 중지 → X

→ TTS 파이썬 파일에 해당 단어를 받아야 하기 때문에 사용 필수

3. Cpython으로 재구현 → X

python 코드를 C/C++ 형태로 재구현한 형태의 코드

느린 python코드를 C/C++ 형태로 바꾼 것이기 때문에 속도가 빠름

→ pyqt5 라는 python의 GUI 기능을 사용해야 하기 때문에 사용 불가 (매우 복잡)

테스트코드

main.py (나중에 main.pyx로 바꿈 → 변환 안됨…실패)

def prime_finder_vanilla(amount):
    primes = []
    
    found = 0
    number = 2

    while found < amount:
        for x in primes:
            if number % x == 0:
                break

        else:
            primes.append(number)
            found += 1
                
        number += 1
        
    return primes
    

def prime_finder_optimized(int amount):

    cdef int number, x, found
    cdef int primes[100000]

    amount = min(amount, 100000)

    found = 0
    number = 2
    while found < amount:
        for x in primes[:found]:
            if number % x == 0:
                break

        else:
            primes[found] = number
            found += 1

        number += 1

    return_list = [p for p in primes[:found]]
    return return_list

setup.py

from setuptools import setup
from Cython.Build import cythonize

setup(
    ext_modules=cythonize('main.pyx')
)

터미널에 아래 명령어 입력

python setup.py build_ext -- inplace

명령어 실행 후 나오는 .c 파일 (위의 기존 python 코드를 C언어 방식으로 해석한 것을 뜻함)

결과 비교

import main
import time

# 기존 python 코드 실행 속도
start_vanilla = time.time()
print(main.prime_finder_vanilla(10000))
end_vanilla = time.time()

print(end_vanilla - start_vanilla)  # 약 3.2초 ~

# Cpython으로 변환한 코드 실행 속도
start_c = time.time()
print(main.prime_finder_optimized(10000))
end_c = time.time()

print(end_c - start_c)  # 약 0.3초~

위의 방법들을 이용해 pyqt5 메인 프로그램의 유사도 측정 동작 부분을 더 빠른 속도로 동작할 수 있도록 하려고 시도하였으나, 제대로 되지 않아 실패

6) 메인프로그램 내 모델 v4와 v5_50epoch비교

기존 학습을 진행한 siamese_net_v4 모델과 새로 진행한 siamese_net_v5_50epoch 모델을 동시 적용하여 정확도가 얼마나 향상되었는지 비교를 진행 하였다.

비교를 진행하기위해 준비 사항

  • siamese_net_v5_50epoch.pt 모델 다운
  • 메인프로그램 내부에 해당 모델 적용 및 검사 결과 출력 문구 추가
model_50epoch = torch.load("siamese_net_v5_50epoch.pt",map_location=device)
# 모델 불러오기
output1_v50, output2_v50 = model_50epoch(x0, x1)
euclidean_distance_v50 = F.pairwise_distance(output1_v50,output2_v50)
final_similar_score_v50 = getScore(euclidean_distance_v50.item())
# 같은 이미지 파일 모델 적용
print(final_similar_score_v50)# 모델 결과 출력

기존 모델과 새로 적용한 모델에 들어간 이미지 파일

Mel_VAD_record

Mel_VAD_TTS_record

모델 결과

  • siamese_net_v4의 경우 89% 일치한다 출력
  • siamese_net_v5_50epoch의 경우 73% 일치한다 출력

육안으로 이미지를 비교해보았을 때 siamese_net_v5_50epoch의 결과가 더 정확하단것을 확인.

이후 진행할 메인프로그램의 경우 v5_50epoch를 적용.

프로그램 작동 확인.

7) 노이즈 제거 코드 적용

  1. noise_filter.py 새로 파일 제작
from pydub import AudioSegment
import noisereduce as nr
import numpy as np
import matplotlib.pyplot as plt

# 녹음된 파일 불러오기

def filter_noise_wav(file_name):
    audio = AudioSegment.from_wav(file_name)

    noise_sample_duration = 1000
    noise_sample = audio[:noise_sample_duration]

    # AudioSegment를 numpy 배열로 변환하는 함수
    def audiosegment_to_numpy(audio_segment):
        samples = np.array(audio_segment.get_array_of_samples())
        if audio_segment.channels == 2:
            samples = samples.reshape((-1, 2))
        return samples

    # 노이즈 샘플과 전체 오디오를 numpy 배열로 변환
    noise_data = audiosegment_to_numpy(noise_sample)
    data = audiosegment_to_numpy(audio)

    # 오디오의 샘플레이트 가져오기
    rate = audio.frame_rate

    # 노이즈 감소 적용
    reduced_noise = nr.reduce_noise(y=data, sr=rate, y_noise=noise_data)

    # numpy 배열을 다시 AudioSegment로 변환하는 함수
    def numpy_to_audiosegment(data, frame_rate, sample_width, channels):
        return AudioSegment(
            data.tobytes(),
            frame_rate=frame_rate,
            sample_width=sample_width,
            channels=channels
        )

    # 노이즈 감소된 numpy 배열을 다시 AudioSegment로 변환
    reduced_noise_audio = numpy_to_audiosegment(
        reduced_noise,
        frame_rate=rate,
        sample_width=data.dtype.itemsize,
        channels=audio.channels
    )

    # 필터링된 오디오 저장
    reduced_noise_audio.export(file_name,format = "wav")

파일 이름을 통해 접근하여 노이즈 제거하여 파일 저장하는 코드

기존 존재하던 preprocessing.py 파일에 해당 파일 import

이후 noise_filter.filter_noise_wav(original_file) 코드 추가하여 vad 적용하기전에 노이즈가 먼저 제거되도록 설정 → 이를 통해 노이즈 제거 → vad 전처리 → mel 처리 진행 순으로 이루어짐

(준표 : TTS파일도 노이즈 필터 씌우면 정확도 올랐음. but TTS는 VAD적용시킨 다음에 filtering해야함)

프로그램에서 해당 코드 작동하는 것 확인 완료

추가적으로 정확성을 높이기 위해 mel-spectrogram 적용된 이미지파일에서 주파수 범위에 따른 필터링을 거쳐서 확실한 구분이 가도록 진행할 것, + 보정 진행.

  • 이동편균 필터링(전체정인 주파수 범위 필터링)
  • 프리-에멧티브 필터링(고주파를 중점으로 필터링)
  • 스무딩(전체적인 주파수 부드럽게 필터링)

8) 마이크 인식 문제 완전히 해결

기존 마이크를 사용했을 때 마이크 인식이 안되는 문제를 ‘마이크 선을 뺏다가 다시 꽂기’, ‘장치 프로필에서 마이크, 스피커 설정 바꿔주기’등 임시 방편으로 마이크 인식 문제를 해결하였다.

하지만 라즈베리파이의 전원을 껐다가 다시 킬 때마다 위와 같은 설정을 지속해서 해줘야하는 번거로움이 생겼다.

이에 마이크 인식이 안되는 근본적인 원인을 찾고자 하였다.

9) Siamese network 흑백 모델 학습

  • training 데이터
    • 한국인 발화 음성 98장
    • 영어권, 아시아권 외국인 발화 음성 총 96장
  • batch_size : 64, epoch : 100
  • contrastiveLoss margin : 2.0
  • 학습 결과

파일명 : siamese_net_v4_gray_nonVAD.pt

파일명 : siamese_net_v4_gray.pt

siamese_net_v6_gray.pt에서 값이 이상한 데이터 셋을 정제한 다음에 학습을 진행

  • 비고
    • 외국인 녹음데이터 앞 삐~ 안 지웠음
  • 학습 결과

파일명 : siamese_net_v7.pt

단어와 짧은 문장으로 구성된 외국인 발화 데이터와 TTS기준 데이터를 이용하여 학습을 진행하였다.

  • training 데이터
    • TTS 발화 음성 500장
    • 영어권, 아시아권 외국인 발화 음성 총 500장
  • batch_size : 64, epoch : 50
  • contrastiveLoss margin : 2.0

  • 학습 결과

파일명 : siamese_net_beta.pt

  • 학습 결과

파일명 : siamese_net_beta_nonVAD.pt

테스트 결과

  1. siamese_net_v6

    좋은 발음과 안좋은 발음의 점수 편차는 낮지만 좋은 발음일때는 높은 점수 안좋음 발음일 때는 상대적으로 낮은 점수가 나오는 정확도가 괜찮은편

    가끔씩 안 좋은 발음을 했음에도 불구하고 점수가 90점 이상으로 올라가는 경우가 있다

  2. siamese_net_v7

    좋은 발음과 안좋은 발음의 점수 편차도 괜찮은편이고 전체적인 정확도도 우수한편

    하지만 사람이 알아듣기 힘들 정도에 발음을 해도 40점이라는 후한 점수를 주는 경우가 있음

  3. siamese_net_v4_gray

    좋은 발음과 안 좋은 발음의 점수 편차가 제일 큼 하지만 전체적인 정확도가 높은편은 아님

    좋은 발음과 안좋은 발음의 구분은 확실하지만, 좋은 발음과 괜찮은 발음의 구분은 어려운편

  4. siamese_net_v4_gray_nonVAD

    좋은 발음과 안 좋은 발음의 점수 편차가 거의 없음 전체적인 정확도도 많이 떨어짐

    좋은 발음이나 안좋은 발음 모두 높은 점수를 매겨 신뢰성이 떨어짐

  5. siamese_net_beta

    좋은 발음과 안 좋은 발음의 점수 편차가 거의 없음 전체적인 정확도는 보통

    하지만 점수 편차가 거의없고 대부분 낮은 점수가 떠 발음을 점수로 구분하기 어려움

  6. siamese_net_beta_nonVAD

    전체적으로 모든 발음에서 반대의 점수가 적용됨 안좋은 발음은 높은 점수를 매기고 좋은 발음은 안좋은 점수를 매김

    점수 편차는 높은 편이고 정확도가 많이 떨어지는 편

비고