본문 바로가기

매일 TIL

[내일배움캠프 6-2일] 코드카타, RVC 음성합성 AI(1)

달리기 경주

https://school.programmers.co.kr/learn/courses/30/lessons/178871

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

푸는 법은 상당히 간단한 문제.

callings 리스트를 돌면서 해당 이름과 앞사람 이름을 players 리스트에서 찾아 둘의 위치를 바꿔주면 된다.

 

def solution_f(players, callings):
    # callings를 돌면서
    for i in callings:
        # 지금 불린 선수의 인덱스 번호 저장
        index_me = players.index(i)
        # 하나 앞 선수 인덱스 번호 저장
        index_front = players.index(i) - 1

        # 둘을 바꿔줌
        players[index_me], players[index_front] = players[index_front], players[index_me]

    return players

시간초과로 실패.

players 리스트에서 몇번째 인덱스인지 찾은 후 그 인덱스 - 1 한 인자와 위치를 바꿔주려고 했음.

하지만 반복문 안에 index 메소드를 사용하면서 효율성이 떨어지게 되어 시간초과 발생.

인덱스 메소드 없이 몇번째에 위치하였는지를 찾아야 했다.

 

def solution(players, callings):
    # enumerate를 사용해 player: i 형태의 딕셔너리 생성
    players_dict = {player: i for i, player in enumerate(players)}

새로운 방법으로 딕셔너리를 선택.

players_dict를 만들어 이름: 인덱스 형태로 저장해놓고 해당 key를 통해 value만 가져오면 되는 방법.

{'mumu': 0, 'soe': 1, 'poe': 2, 'kai': 3, 'mine': 4} 형태로 저장되게 된다.

 

for i in callings:
    # players 리스트에서 몇번째 인덱스인지를 players_dict를 통해 찾는 것
    index_me = players_dict[i] # 3
    index_front = index_me - 1 # 2

    # players 리스트에서 둘 위치 변경
    players[index_me], players[index_front] = players[index_front], players[index_me]

이후 반복문을 통해 callings를 돌면서 해당하는 이름에 대한 인덱스 번호를 players_dict로부터 가져옴.

-1을 한 앞사람 인덱스 역시 저장 후 players 리스트에서 순서를 바꿔주었다.

 

# 딕셔너리 갱신을 위한 이름 저장
name_me = players[index_me] # 'kai'
name_front = players[index_front] # 'poe'

# 딕셔너리에서도 둘의 value 변경
players_dict[name_me] = index_front
players_dict[name_front] = index_me

그런데 리스트에서 달리기 순서가 바뀌면 결국 인덱스도 바뀌는 것이므로 players_dict의 밸류역시 바꿔주어야 함.

key값인 이름을 받기 위해 나의 이름(name_me)와 앞사람 이름(name_front)을 저장.

이후 해당 key에 대한 value를 서로 바꿔주면 끝.

 

def solution(players, callings):
    # enumerate를 사용해 player: i 형태의 딕셔너리 생성
    players_dict = {player: i for i, player in enumerate(players)}
    # {'mumu': 0, 'soe': 1, 'poe': 2, 'kai': 3, 'mine': 4}

    for i in callings:
        # players 리스트에서 몇번째 인덱스인지를 players_dict를 통해 찾는 것
        index_me = players_dict[i] # 3
        index_front = index_me - 1 # 2

        # 딕셔너리 갱신을 위한 이름 저장
        name_me = players[index_me] # 'kai'
        name_front = players[index_front] # 'poe'

        # players 리스트에서 둘 위치 변경
        players[index_me], players[index_front] = players[index_front], players[index_me]

        # 딕셔너리에서도 둘의 value 변경
        players_dict[name_me] = index_front
        players_dict[name_front] = index_me

    return players

최종 코드


RVC

Retrieval-Based Voice Conversion

RVC를 활용하여 음성변조를 쉽게 할 수 있음. 인공지능&빅데이터 과정이라 한번 오랜만에 해봤다.

흔히 말하는 임재범이 부르는 하입보이 뭐 그런거라고 생각하면 됨.

 

1. 음성 데이터 수집

내 음성을 5분정도 녹음하는데 이때 낮은 음역대부터 높은 음역대 모두 녹음이 잘 되어야 함.(저음 + 고음 데이터)

간단히 테스트하는 차원에서 진행했기 때문에 5분 정도의 길이로 했다.

경험상 15초 분량 기준으로 적어도 50개 이상 학습시켜야 하고(총 10~12분 분량), 이때 평소 말하는 음성 데이터와 실제로 노래를 부르는 음성 데이터가 모두 학습되는 것이 좋다.

 

2. 데이터 전처리

RVC 자체적으로 노이즈 처리, 무음 부분을 감지하여 제거, 음성을 일괄적으로 분할, Normalization까지 하는 것으로 알고 있음. 하지만 이 부분에 대해 확실하지 않아서 녹음한 음성 데이터의 노이즈를 직접 제거한 뒤 15초씩 잘라서 저장해줬다.

 

3. 모델 훈련 시작

한 번의 모델 업데이트 단계 당 batch_size개의 데이터를 탐색하여 예측과 오차를 수정.

데이터셋 전부에 대해 이 작업을 한 번 수행하는데 이를 하나의 epoch라고 계산.

즉 학습 시간은 단계 당 학습 시간 * (데이터셋 내 데이터의 수 / batch_size) * epoch

지금 테스트기 때문에 epoch를 상대적으로 낮은 100으로 설정.

batch_size는 5로 설정하였음.

TIL 작성하는 지금 기준으로 epoch 67 진행중. 4시에 시작했는데 아마 10시가 지나야 끝날 것 같다. 

이래서 학습을 위해서라도 좋은 그래픽카드로 바꿔야 하는거...

 

흔히 모델 훈련에서 수만개의 데이터들을 학습하고 처리하는 과정에 비해 RVC는 상당히 적은 데이터셋으로 진행되는 것을 알 수 있는데 이는 RVC가 사전 훈련된 가중치에서 모델 훈련을 시작하기 때문. 학습 시 모델 파라미터를 저장하게 되는데 경로를 지정함에 따라 다른 테스트에서 학습한 모델의 가중치에서 학습을 시작할 수도 있다.

 

오늘 변조 결과까지 확인하려했지만 실패.

내일 TIL에 마저 작성해야겠다.


오늘의 회고

오랜만에 AI 관련 특강을 들으니 재미있었다.

잘 집중해서 들을 수 있었다. 학습 과정을 복습할 겸 간단한 음성변조 실습을 진행했다.

스쿼드 과제 해야하는데 큰일이다. 튜터님 죄송합니다.

 

내일의 목표는 코드카타 + CS특강 듣기 + 스쿼드 과제 하나만 하기 진짜로.