ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Tacotron 무지성 구현 - 5/N
    Tacotron 1 2021. 7. 30. 13:41

    Decoder와 Post CBHG를 구현하다가 골머리 썩히고 왔습니다.

     

    논문에서 Teacher Forcing을 사용하지 않은 줄 알았는데,

    알고보니 사용했더라구요 :(

     

    Teacher Forcing을 사용하려면 Reduction Factor의 배수에 맞게

    Pre-processing 부분에서 알맞게 패딩을 해줘야하는데

    저는 이 부분에 대해서 진행하지 않았기 때문에

    많은 에러를 접하고 왔습니다.

     

    따라서 앞서 작성한 포스팅에는 일부 수정해두었으니, 참고 바랍니다.

     

     

     


    Hyper Parameters

     

    Encoder 포스팅에서 다뤘던 Hyper Parameters와 차이점은

    decoder_rnn_dim 하나밖에 없습니다.

     

    각 모듈마다 많은 인자를 다루면

    그만큼 복잡해질뿐더러, 하나의 인자를 변경했을 때

    많은 부분을 수정해야합니다.

     

    어떻게 보면 효율적이지만,

    다르게 보면 의도한대로 잘 동작하지 않을 수 있으므로

    유의해야할 것 같습니다.

    import os
    
    class Hparams():
        # speaker name
        speaker = 'KSS'
        
        # Audio Pre-processing
        origin_sample_rate = 44100
        sample_rate = 22050
        n_fft = 1024
        hop_length = 256
        win_length = 1024
        n_mels = 80
        reduction = 5
        n_specs = n_fft // 2 + 1
        fmin = 0
        fmax = sample_rate // 2
        min_level_db = -80
        ref_level_db = 0
        
        # Text Pre-processing
        PAD = '_'
        EOS = '~'
        SPACE = ' '
        SPECIAL = '.,!?'
        JAMO_LEADS = "".join([chr(_) for _ in range(0x1100, 0x1113)])
        JAMO_VOWELS = "".join([chr(_) for _ in range(0x1161, 0x1176)])
        JAMO_TAILS = "".join([chr(_) for _ in range(0x11A8, 0x11C3)])
        symbols = PAD + EOS + JAMO_LEADS + JAMO_VOWELS + JAMO_TAILS + SPACE + SPECIAL
    
        _symbol_to_id = {s: i for i, s in enumerate(symbols)}
        _id_to_symbol = {i: s for i, s in enumerate(symbols)}
        
        # Pre-processing paths (text, mel, spec)
        data_dir = os.path.join('data')
        out_texts_dir = os.path.join(data_dir, 'texts')
        out_mels_dir = os.path.join(data_dir, 'mels')
        out_specs_dir = os.path.join(data_dir, 'specs')
        
        # Embedding Layer
        in_dim = 256
        
        # Encoder Pre-net Layer
        prenet_dropout_ratio = 0.5
        prenet_linear_size = 256
    
        # CBHG
        cbhg_K = 16
        cbhg_mp_k = 2
        cbhg_mp_s = 1
        cbhg_mp_p = 1
        cbhg_mp_d = 2
        cbhg_conv_proj_size = 128
        cbhg_conv_proj_k = 3
        cbhg_conv_proj_p = 1    
        cbhg_gru_hidden_dim = 128
        
        # Decoder
        decoder_rnn_dim = 256

     

     


    Decoder

     

    Attention, Decoder, Post CBHG, Griffin-Lim 총 네 가지로 분류해서 볼 수 있습니다. 이번 포스팅에서는 Griffin-Lim은 다루지 않겠습니다.

     

    역시 오늘도 참고할 Table 1 입니다. Decoder PreNet, AttentionRNN, DecoderRNN, Post CBHG 순서대로 코드를 작성할 예정입니다.

     

     

     


    Decoder PreNet

     

    첫 번째 Layer는 Attention이 아닌 Decoder PreNet입니다.

     

    왜냐하면, Mel 하나의 Frame에 맞게 생성된 데이터는

    Attention에 입력되기 전에

    Decoder PreNet Layer와 AttentionRNN Layer를 지나가기 때문이죠.

     

    앞서 작성했던 포스팅에서 PreNet을 다뤘을 때

    인자 중 user_decoder=False를 작성했던 것을 보셨을겁니다.

    이번 Decoder에 사용되는 PreNet은 use_decoder 인자를

    True로 바꿔서 사용하게 됩니다.

     

    큰 차이점은 없고,

    use_decoder=True로 사용 시 self.prenet_input_dim shape가

    self.Hparams.n_mels*self.Hparams.reduction으로 변경됩니다.

     

    왜냐하면, 논문에서는 Reduction Factor를 사용해서

    Mel Frame을 Reduction Factor 배수만큼 생성했을 때

    그만큼 효과를 보았다고 기재해두었기 때문에

    저 또한 논문에 맞게 Input shape을 수정했습니다.

    논문에서는 Reduction Factor(r)을 2로 사용했다고 하지만, 5로 사용했을 때에도 좋은 효과를 볼 수 있었다고 하므로 저는 5로 사용하겠습니다.

    class PreNet(torch.nn.Module):
        def __init__(self, Hparams, use_decoder=False):
            super(PreNet, self).__init__()
            self.Hparams = Hparams
            
            self.prenet_input_dim = self.Hparams.in_dim
            if use_decoder==True:
                self.prenet_input_dim = self.Hparams.n_mels*self.Hparams.reduction
                
            in_sizes = [self.prenet_input_dim] + [self.Hparams.prenet_linear_size]
            out_sizes = [self.Hparams.prenet_linear_size] + [self.Hparams.prenet_linear_size//2]
    
            layers = []
            for (in_features, out_features) in zip(in_sizes, out_sizes):
                layers += [CustomLinear(in_features=in_features, 
                                        out_features=out_features,
                                        activation='relu',
                                        dropout=self.Hparams.prenet_dropout_ratio)]
    
            self.prenet = torch.nn.Sequential(*layers)
    
        def forward(self, x):
            return self.prenet(x)

     

    앞서 언급했듯이 Decoder PreNet의 첫 번째 Input은

    Mel이 아닌 Mel의 첫 번째 Frame에 맞는 dim을 가진 데이터입니다.

    이게 무슨 말이냐면,

    Teacher Forcing의 입력값을 제외하고

    Text와 Mel Lengths이 전부일텐데,

    Text는 Encoder 입력값이고,

    Mel Lengths는 Decoder가 생성하는 Frame의 Length의 기준을

    잡아주는 역할을 하는 것일 뿐입니다.

     

    그리고 Attention에서 나온 출력값은

    DecoderRNN과 AttentionRNN에서 나온 값과 계산되어 Frame을 생성합니다.

     

    그러면 도대체 Decoder는 처음에 어떤 것을 입력을 받고 Frame생성을 시작할까요?

     

    위의 그림에 그려져있는 화살표를 보면

    첫 번째 화살표에 <GO> frame이라고 명시 되어 있는 부분이

    처음 Decoder에 입력되는 입력값입니다.

     

    아래의 이미지에서 볼 수 있듯이,

    <GO> Frame은 zero-frame으로 생성되며

    매 step 마다 Decoder의 첫 입력 값으로 사용됩니다.

    논문 5페이지 맨 상단에 있는 문구입니다.
    같은 문단에 있는 문구입니다. 논문에서는 학습하는 동안 Reduction Factor(r) 번째에 있는 Ground Truth Frame을   Decoder에 입력했다고 합니다.

     

    Decoder 구현 시 Teacher Forcing에 대한 사용 여부와

    Teacher Forcing을 사용할 때

    Input을 zero-frame과 Ground Truth간에 서로 바꾸게끔 하는

    기능을 잘 구현해야 할 것 같습니다.

     

     

    Decoder PreNet의 첫 입력은 <GO> frame에 해당하는 부분이므로

    아래의 코드 블럭과 같이 변수를 지정합니다.

    init_dec_prenet_input = encoded.data.new(B, hparams.n_mels*hparams.reduction).zero_()

     

    Decoder PreNet에 init_dec_prenet_input을 입력하고 출력해봅니다.

    dataset = KSSDataset(hparams)
    
    dataloader = torch.utils.data.DataLoader(dataset, 
                                             batch_size=4, 
                                             shuffle=True, 
                                             collate_fn=collate_fn)
    
    encoder = Encoder(hparams, use_decoder=False, use_post=False)
    encoded = encoder(texts)
    B = texts.size(0)
    init_dec_prenet_input = encoded.data.new(B, hparams.n_mels*hparams.reduction).zero_()
    
    dec_prenet = PreNet(hparams, use_decoder=True)
    
    for step, values in enumerate(dataloader):
        _, _, _, _ = values
        dec_prenet_result = dec_prenet(init_dec_prenet_input)
        
        print('Decoder PreNet result: {} \n'.format(
            dec_prenet_result.size()))
              
        if step == 0:
            break
            
    
    ####
    Decoder PreNet result: torch.Size([4, 128])
    dec_prenet = PreNet(hparams, use_decoder=True)
    
    print(dec_prenet)
    
    
    ####
    PreNet(
      (prenet): Sequential(
        (0): CustomLinear(
          (linear): Linear(in_features=400, out_features=256, bias=True)
          (activation): ReLU()
          (dropout): Dropout(p=0.5, inplace=False)
        )
        (1): CustomLinear(
          (linear): Linear(in_features=256, out_features=128, bias=True)
          (activation): ReLU()
          (dropout): Dropout(p=0.5, inplace=False)
        )
      )
    )

     

     


    AttentionRNN

     

    Decoder PreNet 다음은 AttentionRNN Layer 입니다.

    AttentionRNN Layer는 위의 이미지에서 볼 수 있다시피,

    Attention Layer에 출력을 전달합니다.

    그리고 DecoderRNN과 그 다음 AttentionRNN에

    각각의 현재 Output과 Hidden state를 그 다음 Layer에 전달합니다.

     

    다시 말하면,

    처음 frame 생성을 시작하는 AttentionRNN도,

    DecoderRNN도

    각자 입력받는 전 단계의 Output과 Hidden state가 있다는 말입니다.

     

    아래 두 번째 코드 블럭을 보시면,

    init_attn_rnn_state가 zero값으로

    Input shape에 맞게 지정되어있는 것을 볼 수 있습니다.

     

    다만, Table 1에서 볼 수 있듯이

    1-layer GRU (256cells) 라고 기재되어있으므로

    그에 상응하게끔 torch.nn.GRU()의 hidden_size 인자 값을

    알맞게 입력해줍니다.

    class AttentionRNN(torch.nn.Module):
        def __init__(self, Hparams):
            super(AttentionRNN, self).__init__()
            self.Hparams = Hparams
            
            self.attention_rnn = torch.nn.GRU(input_size=self.Hparams.prenet_linear_size//2, # 128
                                              hidden_size=self.Hparams.cbhg_gru_hidden_dim*2, # 256
                                              num_layers=1,
                                              batch_first=True,
                                              bidirectional=False)
        
        def forward(self, x, h):
        	# Add and Convert Time axis
            # (B, Attn_dim) -> (B, Attn_dim, 1) -> (B, 1, Attn_dim)
            x = x.unsqueeze(-1).permute(0,2,1) 
            x, h = self.attention_rnn(x, h)
            # h: attention 의 query 값으로 들어감
            return x, h

    위 부분은 torch.nn.GRU() 모듈로 구성되어 있지만

    실제로는 torch.nn.GRUCell() 모듈이 사용되어야합니다.

    (추후에 수정 예정)

     

    encoder = Encoder(hparams, use_decoder=False, use_post=False)
    encoded = encoder(texts)
    
    B = encoded.size(0)
    init_attn_rnn_input = encoded.data.new(B, hparams.n_mels*hparams.reduction).zero_()
    init_attn_rnn_state = encoded.data.new(1, B, hparams.cbhg_gru_hidden_dim*2).zero_()
    
    dec_prenet = PreNet(hparams, use_decoder=True)
    attention_rnn = AttentionRNN(hparams)
    
    
    dec_prenet_result = dec_prenet(init_attn_rnn_input)
    attention_rnn_result, current_attn_rnn_state = attention_rnn(dec_prenet_result, 
                                                                 init_attn_rnn_state)
    
    print('Initial Decoder input: {} \nInitial Decoder Hidden sate: {} \n'.format(
        init_dec_attn_rnn_input.size(), 
        init_dec_attn_rnn_state.size()))
    
    print('Encoder result: {} \nDecoder PreNet result: {} \nAttentionRNN result: {} \nCurrent AttentionRNN h-state: {} \n'.format(
        encoded.size(),
        dec_prenet_result.size(), 
        attention_rnn_result.size(), 
        current_attn_rnn_state.size()))
            
            
    ####
    Initial Decoder input: torch.Size([4, 400]) 
    Initial Decoder Hidden sate: torch.Size([1, 4, 256]) 
    
    Encoder result: torch.Size([4, 50, 256]) 
    Decoder PreNet result: torch.Size([4, 128]) 
    Decoder AttentionRNN result: torch.Size([4, 1, 256]) 
    Current Hidden state: torch.Size([1, 4, 256])
    dec_attn_rnn = AttentionRNN(hparams)
    
    print(dec_attn_rnn)
    
    
    ####
    AttentionRNN(
      (attention_rnn): GRU(128, 256, batch_first=True)
    )

     

     

     


    Attention & Attention Wrapper

     

    Attention Layer 구현 관련된 부분은

    "텐서플로2와 머신러닝으로 시작하는 자연어처리" 도서를 참고했습니다.

    아래에 github 링크를 남겨둡니다.

    https://github.com/NLP-kr/tensorflow-ml-nlp-tf2

     

    GitHub - NLP-kr/tensorflow-ml-nlp-tf2: 텐서플로2와 머신러닝으로 시작하는 자연어처리 (로지스틱회귀부

    텐서플로2와 머신러닝으로 시작하는 자연어처리 (로지스틱회귀부터 BERT와 GPT2까지) 실습자료 - GitHub - NLP-kr/tensorflow-ml-nlp-tf2: 텐서플로2와 머신러닝으로 시작하는 자연어처리 (로지스틱회귀부터

    github.com

     

    Attention Wrapper는 간단한 클래스로 구현했습니다.

    구글링해봐도 계산과정이 각기 달라서

    저는 그냥 더하는 것으로 했습니다.

    코더가 되지 않기 위해 논문을 주로 보려고 노력하는데

    이 부분은 조금 난해하네요.

    Attention output인 Context Vector와

    AttentionRNN cell output 값을 concatenate 했다고 하는데

    이 부분 잘 아시는 분 있으시면 댓글 부탁드립니다.

     

    # Attention
    class BahdanauAttention(torch.nn.Module):
        def __init__(self, Hparams):
            super(BahdanauAttention, self).__init__()
            self.Hparams = Hparams
            
            self.W1 = CustomLinear(in_features=self.Hparams.cbhg_gru_hidden_dim*2,# 256
                                   out_features=self.Hparams.cbhg_gru_hidden_dim, # 128
                                   activation=None,
                                   dropout=None) # values: encoder output (batch_size, num_timesteps, units)
            
            self.W2 = CustomLinear(in_features=self.Hparams.cbhg_gru_hidden_dim*2,# 256
                                   out_features=self.Hparams.cbhg_gru_hidden_dim, # 128
                                   activation=None,
                                   dropout=None) # query: decoder attention rnn hidden state (num, batch_size, units)
            
            self.V = CustomLinear(in_features=self.Hparams.cbhg_gru_hidden_dim,
                                  out_features=1,
                                  activation=None,
                                  dropout=None)
    
        def forward(self, query, values): 
            # convert to time axis from num axis which is num of gru layers
            query_with_time_axis = query.permute(1,0,2) 
    
            # scoring
            score = self.V(torch.tanh(self.W1(values) + self.W2(query_with_time_axis)))
    
            # softmax
            # (batch_size, num_timesteps, self.V out_features)
            alignment = torch.softmax(score, dim=1) 
    
            # calculating with attention
            # transpose: (batch_size, cbhg_gru_hidden_dim, num_timesteps)
            # matmul: (batch_size, cbhg_gru_hidden_dim, encoder_dim)
            # reduce_sum: (64, 1024) (batch_size, cbhg_gru_hidden_dim)
            # expand_dim: (batch_size, time_axis, encoder_dim)
            context = torch.sum(torch.matmul(alignment.permute(0,2,1), values), dim=1).unsqueeze(1)
            return context, alignment
        
           
    # Attention Wrapper
    class AttentionWrapper(torch.nn.Module):
        def __init__(self):
            super(AttentionWrapper, self).__init__()
        
        def forward(self, context, attention_rnn_result): # values: attention values, key: decoder inputs
            return context + attention_rnn_result

     

    Attention Layer까지 고려한 출력은 아래와 같습니다.

     

    encoder = Encoder(hparams, use_decoder=False, use_post=False)
    encoded = encoder(texts)
    
    B = encoded.size(0)
    init_attn_rnn_input = encoded.data.new(B, hparams.n_mels*hparams.reduction).zero_()
    init_attn_rnn_state = encoded.data.new(1, B, hparams.cbhg_gru_hidden_dim*2).zero_()
    
    dec_prenet = PreNet(hparams, use_decoder=True)
    attention_rnn = AttentionRNN(hparams)
    attention = BahdanauAttention(hparams)
    attention_wrapper = AttentionWrapper()
    
    dec_prenet_result = dec_prenet(init_attn_rnn_input)
    attention_rnn_result, current_attn_rnn_state = attention_rnn(dec_prenet_result, 
                                                                 init_attn_rnn_state)
    context, alignment = attention(current_attn_rnn_state, encoded)
    attention_result = attention_wrapper(context, attention_rnn_result)
    
    print('Initial Decoder input: {} \nInitial Decoder Hidden sate: {} \n'.format(
        init_dec_attn_rnn_input.size(), 
        init_dec_attn_rnn_state.size()))
    
    print('Encoder result: {} \nDecoder PreNet result: {} \nAttentionRNN result: {} \nCurrent AttentionRNN h-state: {} \nContext Vector: {} \nAlignment: {} \nAttention result: {} \n'.format(
        encoded.size(),
        dec_prenet_result.size(), 
        attention_rnn_result.size(), 
        current_attn_rnn_state.size(),
        context.size(),
        alignment.size(),
        attention_result.size()))
        
        
    ####
    Initial Decoder input: torch.Size([4, 400]) 
    Initial Decoder Hidden sate: torch.Size([1, 4, 256]) 
    
    Encoder result: torch.Size([4, 50, 256]) 
    Decoder PreNet result: torch.Size([4, 128]) 
    AttentionRNN result: torch.Size([4, 1, 256]) 
    Current AttentionRNN h-state: torch.Size([1, 4, 256]) 
    Context Vector: torch.Size([4, 1, 256]) 
    Alignment: torch.Size([4, 50, 1]) 
    Attention result: torch.Size([4, 1, 256])

     

     

     

    <Attention Layer & AttentionWrapper 수정>

    # Attention
    class BahdanauAttention(torch.nn.Module):
        def __init__(self):
            super(BahdanauAttention, self).__init__()
    
        def forward(self, query, values): 
            if (query.size(0) != values.size(0)):
                query_with_time_axis = query.permute(1,0,2) 
            elif (query.size(0) == values.size(0)):
                query_with_time_axis = query
    
            score = torch.matmul(query_with_time_axis, values.permute(0,2,1))
            alignment = torch.softmax(score.permute(0,2,1), dim=1)
            context = torch.matmul(alignment.permute(0,2,1), values)
            return context, alignment
    
    
    # Attention Wrapper
    class AttentionWrapper(torch.nn.Module):
        def __init__(self):
            super(AttentionWrapper, self).__init__()
        
        def forward(self, context, query):
            return torch.cat((context, query), dim=-1)
    encoder = Encoder(hparams)
    encoded = encoder(texts)
    
    B = encoded.size(0)
    init_attn_rnn_input = encoded.data.new(B, hparams.n_mels*hparams.reduction).zero_()
    init_attn_rnn_state = encoded.data.new(1, B, hparams.cbhg_gru_hidden_dim*2).zero_()
    
    dec_prenet = PreNet(hparams, use_decoder=True)
    attention_rnn = AttentionRNN(hparams)
    attention = BahdanauAttention()
    attention_wrapper = AttentionWrapper()
    
    dec_prenet_result = dec_prenet(init_attn_rnn_input)
    attention_rnn_result, current_attn_rnn_state = attention_rnn(dec_prenet_result, 
                                                                 init_attn_rnn_state)
    # context, alignment = attention(current_attn_rnn_state, encoded)
    context, alignment = attention(attention_rnn_result, encoded)
    attention_result = attention_wrapper(context, attention_rnn_result)
    
    print('Initial Decoder input: {} \nInitial Decoder Hidden sate: {} \n'.format(
        init_attn_rnn_input.size(), 
        init_attn_rnn_state.size()))
    
    print('Encoder result: {} \nDecoder PreNet result: {} \nAttentionRNN result: {} \nCurrent AttentionRNN h-state: {} \nContext Vector: {} \nAlignment: {} \nAttention result: {} \n'.format(
        encoded.size(),
        dec_prenet_result.size(), 
        attention_rnn_result.size(), 
        current_attn_rnn_state.size(),
        context.size(),
        alignment.size(),
        attention_result.size()))
        
        
    ####
    Initial Decoder input: torch.Size([4, 400]) 
    Initial Decoder Hidden sate: torch.Size([1, 4, 256]) 
    
    Encoder result: torch.Size([4, 50, 256]) 
    Decoder PreNet result: torch.Size([4, 128]) 
    AttentionRNN result: torch.Size([4, 1, 256]) 
    Current AttentionRNN h-state: torch.Size([1, 4, 256]) 
    Context Vector: torch.Size([4, 1, 256]) 
    Alignment: torch.Size([4, 50, 1]) 
    Attention result: torch.Size([4, 1, 512])

    DecoderRNN

     

    DecoderRNN에서 Residual Connection을 진행했다고 합니다.

    따라서 저는 이전 Encoder CBHG 구현할 때 사용했던 방법을

    그대로 채택하겠습니다.

    -> Residual Connection은 Decoder에서 직접 진행

    class DecoderRNN(torch.nn.Module):
        def __init__(self, Hparams):
            super(DecoderRNN, self).__init__()
            self.Hparams = Hparams
            self.residual = torch.nn.Identity()
            self.decoder_rnn = torch.nn.GRU(input_size=self.Hparams.cbhg_gru_hidden_dim*2,
                                            hidden_size=self.Hparams.decoder_rnn_dim,
                                            num_layers=2,
                                            batch_first=True,
                                            bidirectional=False)
    
        def forward(self, x, h):
            short_cut = self.residual(x)
            x, h = self.decoder_rnn(x, h)
            x = x + short_cut
            return x, h

     

    위 코드에서 torch.nn.GRU() 모듈이 사용되었지만,

    실제로는 torch.nn.GRUCell() 모듈이 사용되어야 합니다.

    (추후에 수정 예정)

     

    DecoderRNN까지 고려한 출력은 아래와 같습니다.

     

    encoder = Encoder(hparams, use_decoder=False, use_post=False)
    encoded = encoder(texts)
    
    B = encoded.size(0)
    init_attn_rnn_input = encoded.data.new(B, hparams.n_mels*hparams.reduction).zero_()
    init_attn_rnn_state = encoded.data.new(1, B, hparams.cbhg_gru_hidden_dim*2).zero_()
    init_dec_rnn_state = encoded.data.new(2, B, hparams.cbhg_gru_hidden_dim*2).zero_()
    
    dec_prenet = PreNet(hparams, use_decoder=True)
    attention_rnn = AttentionRNN(hparams)
    attention = BahdanauAttention(hparams)
    attention_wrapper = AttentionWrapper()
    decoder_rnn = DecoderRNN(hparams)
    
    dec_prenet_result = dec_prenet(init_attn_rnn_input)
    attention_rnn_result, current_attn_rnn_state = attention_rnn(dec_prenet_result, 
                                                                 init_attn_rnn_state)
    context, alignment = attention(current_attn_rnn_state, encoded)
    attention_result = attention_wrapper(context, attention_rnn_result)
    dec_rnn_result, current_dec_rnn_state = decoder_rnn(attention_result, 
                                                init_dec_decoder_rnn_state)
    
    print('Initial Decoder input: {} \nInitial Decoder Hidden sate: {} \n'.format(
        init_dec_attn_rnn_input.size(), 
        init_dec_attn_rnn_state.size()))
    
    print('Encoder result: {} \nDecoder PreNet result: {} \nAttentionRNN result: {}\
            \nCurrent AttentionRNN h-state: {} \nCurrent DecoderRNN h-state: {} \nContext Vector: {}\
            \nAlignment: {} \nAttention result: {} \nDecoderRNN result: {} \n'.format(
        encoded.size(),
        dec_prenet_result.size(), 
        attention_rnn_result.size(), 
        current_attn_rnn_state.size(),
        current_dec_rnn_state.size(),
        context.size(),
        alignment.size(),
        attention_result.size(),
        dec_rnn_result.size()))
        
        
    ####
    Initial Decoder input: torch.Size([4, 400]) 
    Initial Decoder Hidden sate: torch.Size([1, 4, 256]) 
    
    Encoder result: torch.Size([4, 50, 256]) 
    Decoder PreNet result: torch.Size([4, 128]) 
    AttentionRNN result: torch.Size([4, 1, 256])        
    Current AttentionRNN h-state: torch.Size([1, 4, 256]) 
    Current DecoderRNN h-state: torch.Size([2, 4, 256]) 
    Context Vector: torch.Size([4, 1, 256])        
    Alignment: torch.Size([4, 50, 1]) 
    Attention result: torch.Size([4, 1, 256]) 
    DecoderRNN result: torch.Size([4, 1, 256])
    decoder_rnn = DecoderRNN(hparams)
    
    print(decoder_rnn)
    
    
    ####
    DecoderRNN(
      (residual): Identity()
      (decoder_rnn): GRU(256, 256, num_layers=2, batch_first=True)
    )

     


    Decoder

     

    대망의 Decoder 입니다.

     

    한번에 머릿속에서 짜잔 하고 

    지식이 쏟아져 내려왔으면 좋겠지만,

    저는 많은 시행착오 끝에 어찌저찌 구현은 했습니다.

     

    그래도 Attention 부분에서 조금 확실하지 않은 부분이 있어서

    완벽한 답안은 아닌 것 같습니다.

     

    그래도 Input Ouput shape은 의도한대로 됐으니

    일단 진행하겠습니다.

     

     

    앞서 써내려왔던 것 중에 못 보던 글자들이 있습니다.

    __init__ 부분에 있는 teacher_forcing=True 인자를 지정한 것과

    self.proj_to_mel Layer를 정의한 부분이 조금 다르네요.

     

    teacher_forcing=True 인자는 말 그대로

    teacher_forcing을 할지 말지 결정하는 인자이면서

    입력이 잘못 들어왔을 때를 대비한 인자이기도 합니다.

     

    teacher_forcing=True로 지정하고, ground_truth를 입력하지 않아도

    기존 모델 구조에 따른 방식을 채택하게끔 했습니다.

    그 반대로 해도 기본 구조를 따르고,

    무조건 teacher_forcing과 ground_truth에 대한 입력을 해주어야

    Teacher Forcing이 진행됩니다.

     

    self.proj_to_mel Layer는

    Post CBHG에 입력되기 전 단계의 Layer라고 보시면 될 것 같습니다.

    다만 유의할 점은 Output shape가

    self.Haprams.n_mels*self.Hparams.reduction

    이라는 부분을 잘 보시면 좋을 듯 합니다.

     

    forward 부분을 보면

    encoded(Encoder Output)와 

    mel_lengths(T값을 결정하는데 사용됨)

    ground_truth(True Mel)이 있습니다.

     

    그리고 if 문이 바로 이어지는데

    Teacher Forcing 사용 시

    Ground Truth의 shape을 손보기 위한 부분입니다.

    shape을 (T, B, n_mels*reduction)으로 변경하는데,

    T값은 전체 프레임에 해당하는 부분이므로

    프레임 하나하나를 편리하게 Ground Truth로

    이용하기 위함이라고 보면됩니다.

     

    해당 if 문에서 조건에 해당하는 부분은 그 밑에도 이어집니다.

    (하나만 사용하고 싶었는데

    뭔가 번뜩 떠오르는 아이디어가 없었네요.)

     

    class Decoder(torch.nn.Module):
        def __init__(self, Hparams, use_decoder=True, teacher_forcing=True):
            super(Decoder, self).__init__()
            self.Hparams = Hparams
            self.use_decoder=use_decoder
            self.teacher_forcing=teacher_forcing
            
            self.dec_prenet = PreNet(Hparams, use_decoder=self.use_decoder)
            self.attention_rnn = AttentionRNN(self.Hparams)
            self.attention = BahdanauAttention()
            self.attention_wrapper = AttentionWrapper()
            self.pre_decoder_rnn = CustomLinear(in_features=self.Hparams.cbhg_gru_hidden_dim*4,
                                            out_features=self.Hparams.decoder_rnn_dim,
                                            activation=None,
                                            dropout=None)
            self.residual = torch.nn.Identity()
            self.decoder_rnn = DecoderRNN(self.Hparams)
            self.proj_to_mel = CustomLinear(in_features=self.Hparams.decoder_rnn_dim,
                                            out_features=self.Hparams.n_mels*self.Hparams.reduction,
                                            activation=None,
                                            dropout=None)
            
        def forward(self, encoded, mel_lengths, ground_truth=None):
            B = encoded.size(0)
            T = torch.max(mel_lengths) // self.Hparams.reduction
            
            if (self.teacher_forcing == True and ground_truth != None):
                decoder_in = ground_truth.view(B,-1,self.Hparams.n_mels*self.Hparams.reduction)
                decoder_in = decoder_in.permute(1,0,2) # (B, T, n_mels) -> (T, B, n_mels)
            
            decoder_out_list = []
            align_list = []
            t = 0
            
            while True:
                if (t == 0):
                    # initial decoder input (B, n_mels*r)
                    current_dec_in = encoded.data.new(B, self.Hparams.n_mels*self.Hparams.reduction).zero_()
                    current_attn_rnn_h = encoded.data.new(1, B, self.Hparams.cbhg_gru_hidden_dim*2).zero_()
                    current_dec_rnn_h = encoded.data.new(2, B, self.Hparams.cbhg_gru_hidden_dim*2).zero_()
                    
                elif (t > 0):
                    # current decoder input
                    if (self.teacher_forcing == True and ground_truth != None):
                        current_dec_in = decoder_in[t-1]
                    else:
                        current_dec_in = decoder_out_list[-1].squeeze(1)
                
                dec_out = self.dec_prenet(current_dec_in)
                dec_out, current_attn_rnn_h = self.attention_rnn(dec_out, current_attn_rnn_h)
                current_context, align = self.attention(dec_out, encoded) # 2021.08.03: attention input had changed "current_attn_rnn_h" to "dec_out"
                attention_result = self.attention_wrapper(current_context, dec_out)
                dec_out = self.pre_decoder_rnn(attention_result)
                sub_dec_out, current_dec_rnn_h = self.decoder_rnn(dec_out, current_dec_rnn_h)
                short_cut = self.residual(sub_dec_out)
                dec_out = dec_out + short_cut # residual connection
                dec_out = self.proj_to_mel(dec_out)
                
                decoder_out_list.append(dec_out)
                align_list.append(align)
                t += 1
                if (t >= T):
                    break
                
            assert len(decoder_out_list) == T
            mels = torch.cat(decoder_out_list, dim=1).contiguous()
            aligns = torch.cat(align_list, dim=2)
            return mels, aligns

     

     

     

     

    while 문을 보면

    가장 밑 부분에 if (t >= T): 에 해당하는 부분에서

    종료하게끔 설계되어있습니다.

    (T 값을 결정하는 방법은 코드블럭 상단에 있으므로 참고 바랍니다.)

     

    먼저 t = 0 일 때 init 값들을 초기화합니다.

    그리고 init 값들은

    바로 self.dec_prenet()으로 입력되며 한 번의 루프를 마치고

    dec_out은 decoder_out_list에,

    align은 align_list에 담기게 됩니다.

     

    여기서부터가 중요한 부분입니다.

     

    t > 0 일 때

     

    Teacher Forcing을 사용한다면

    teacher_forcing=True 값으로 지정되어있어야하고,

    ground_truth 값으로 mels을 입력해주어야합니다.

     

    그리고 current_dec_in 값은

    이 전 단계(t-1)의 손질된 ground_truth의 값으로 변경되며,

    self.dec_prenet()으로 입력되고

    t = 0 일 때와 같은 루프를 마치게 됩니다.

     

    Teacher Forcing을 사용하지 않는다면

    if (self.teacher_forcing == True and ground_truth != None):

    조건만 만족하지 않으면 됩니다.

     

    그리고 current_dec_in 값은

    이 전 단계(t-1)의 dec_out 값이 담긴

    decoder_out_list[-1] 값으로 변경되며

    self.dec_prenet()으로 입력되고

    t = 0 일 때와 같은 루프를 마치게 됩니다.

     

    아래의 코드 블럭은 teacher_forcing=True로 지정하고

    ground_truth를 mels로 입력하게한 출력 결과입니다.

    dataset = KSSDataset(hparams)
    
    dataloader = torch.utils.data.DataLoader(dataset, 
                                             batch_size=4, 
                                             shuffle=True, 
                                             collate_fn=collate_fn)
    
    encoder = Encoder(hparams, use_decoder=False, use_post=False)
    decoder = Decoder(hparams, use_decoder=True, teacher_forcing=True)
    for step, values in enumerate(dataloader):
        texts, mels, _, mel_lengths = values
        
        encoded = encoder(texts)
        mels, aligns = decoder(encoded, mel_lengths, mels)
        
        print('Encoder result: {} \nDecoder result: {} \nAlignments result: {} \n'.format(
            encoded.size(), mels.size(), aligns.size()))
        
        if step == 4:
            break
    
    
    ####
    Encoder result: torch.Size([4, 49, 256]) 
    Decoder result: torch.Size([4, 75, 400]) 
    Alignments result: torch.Size([4, 49, 75]) 
    
    Encoder result: torch.Size([4, 51, 256]) 
    Decoder result: torch.Size([4, 57, 400]) 
    Alignments result: torch.Size([4, 51, 57]) 
    
    Encoder result: torch.Size([4, 43, 256]) 
    Decoder result: torch.Size([4, 70, 400]) 
    Alignments result: torch.Size([4, 43, 70]) 
    
    Encoder result: torch.Size([4, 47, 256]) 
    Decoder result: torch.Size([4, 66, 400]) 
    Alignments result: torch.Size([4, 47, 66]) 
    
    Encoder result: torch.Size([4, 44, 256]) 
    Decoder result: torch.Size([4, 56, 400]) 
    Alignments result: torch.Size([4, 44, 56])
    decoder = Decoder(hparams, use_decoder=True, teacher_forcing=True)
    
    print(decoder)
    
    
    ####
    Decoder(
      (dec_prenet): PreNet(
        (prenet): Sequential(
          (0): CustomLinear(
            (linear): Linear(in_features=400, out_features=256, bias=True)
            (activation): ReLU()
            (dropout): Dropout(p=0.5, inplace=False)
          )
          (1): CustomLinear(
            (linear): Linear(in_features=256, out_features=128, bias=True)
            (activation): ReLU()
            (dropout): Dropout(p=0.5, inplace=False)
          )
        )
      )
      (attention_rnn): AttentionRNN(
        (attention_rnn): GRU(128, 256, batch_first=True)
      )
      (attention): BahdanauAttention()
      (attention_wrapper): AttentionWrapper()
      (pre_decoder_rnn): CustomLinear(
        (linear): Linear(in_features=512, out_features=256, bias=True)
      )
      (residual): Identity()
      (decoder_rnn): DecoderRNN(
        (decoder_rnn): GRU(256, 256, num_layers=2, batch_first=True)
      )
      (proj_to_mel): CustomLinear(
        (linear): Linear(in_features=256, out_features=400, bias=True)
      )
    )

     

    aligns의 shape을 보면

    (B, Encoder_T, Decoder_T) 이런식으로 되어있습니다.

    Encoder_T는 Sequence 길이에 해당하는 부분이고

    Decoder_T는 방금 정의한 T(from mel_lengths)입니다.

     

    각각의 길이는 나중에

    alignment 그래프를 그릴 때 기준이 됩니다.

    Encoder_T: Y-axis, Decoder_T: X-axis

     

     


    Post CBHG & Linear-Scaling Layer

     

    Post CBHG는 Encoder CBHG를 구현할 때 관련된 언급을 했으므로

    간략하게 설명하고 넘어가겠습니다.

     

    Decoder에서 출력된 Predicted Mels가 Post CBHG로 입력될 때,

    바로 입력되기보다는 shape을 변경해야합니다.

     

    위의 코드 블럭에서 출력을 보면 알 수 있듯이,

    현재의 shape은 (B, -1, n_mels*reduction)와 같습니다.

     

    우리가 원하는 shape은 (B, -1, n_mels) 이므로

    Predicted Mels의 shape을 변경해서 Post CBHG에 입력합니다.

     

    마지막으로 linear_scaling 이라는 변수에 지정된 CustomLinear Layer가 있는데,

    이는 Predicted Mels를 Predicted Specs로 Up-Scaling 하는 과정이라고 보면 됩니다.

     

    Up-Scaling 까지 고려한 출력은 아래와 같습니다.

    dataset = KSSDataset(hparams)
    
    dataloader = torch.utils.data.DataLoader(dataset, 
                                             batch_size=4, 
                                             shuffle=True, 
                                             collate_fn=collate_fn)
    
    encoder = Encoder(hparams, use_decoder=False, use_post=False)
    decoder = Decoder(hparams, use_decoder=True, teacher_forcing=True)
    post_cbhg = CBHG(hparams, use_post=True)
    linear_scaling = CustomLinear(hparams.n_mels*2, hparams.n_specs)
    for step, values in enumerate(dataloader):
        texts, mels, _, mel_lengths = values
        mel_length = torch.max(mel_lengths)
        encoded = encoder(texts)
        pred_mels, aligns = decoder(encoded, mel_length, mels)
        post_cbhg_result = post_cbhg(mels.view(texts.size(0), -1, hparams.n_mels))
        pred_specs = linear_scaling(post_cbhg_result)
        
        print("Encoder result: {} \nMax Batch Mel Length: {}\
            \nDecoder result: {} \nAlignments result: {}\
            \nPost CBHG result: {} \nFinal result: {} \n".format(
            encoded.size(), mel_length, 
            pred_mels.size(), aligns.size(), 
            post_cbhg_result.size(), pred_specs.size()))
        
        if step == 4:
            break
           
    
    ####
    Encoder result: torch.Size([4, 43, 256]) 
    Max Batch Mel Length: 695        
    Decoder result: torch.Size([4, 139, 400]) 
    Alignments result: torch.Size([4, 43, 139])        
    Post CBHG result: torch.Size([4, 695, 160]) 
    Final result: torch.Size([4, 695, 513]) 
    
    Encoder result: torch.Size([4, 42, 256]) 
    Max Batch Mel Length: 580        
    Decoder result: torch.Size([4, 116, 400]) 
    Alignments result: torch.Size([4, 42, 116])        
    Post CBHG result: torch.Size([4, 580, 160]) 
    Final result: torch.Size([4, 580, 513]) 
    
    Encoder result: torch.Size([4, 58, 256]) 
    Max Batch Mel Length: 860        
    Decoder result: torch.Size([4, 172, 400]) 
    Alignments result: torch.Size([4, 58, 172])        
    Post CBHG result: torch.Size([4, 860, 160]) 
    Final result: torch.Size([4, 860, 513]) 
    
    Encoder result: torch.Size([4, 46, 256]) 
    Max Batch Mel Length: 620        
    Decoder result: torch.Size([4, 124, 400]) 
    Alignments result: torch.Size([4, 46, 124])        
    Post CBHG result: torch.Size([4, 620, 160]) 
    Final result: torch.Size([4, 620, 513]) 
    
    Encoder result: torch.Size([4, 44, 256]) 
    Max Batch Mel Length: 610        
    Decoder result: torch.Size([4, 122, 400]) 
    Alignments result: torch.Size([4, 44, 122])        
    Post CBHG result: torch.Size([4, 610, 160]) 
    Final result: torch.Size([4, 610, 513])

     

    아래의 코드 블럭을 실행하면 다음과 같은 Alignment Graph가 생성됩니다.

     

    pred_mels, aligns = decoder(encoded, mel_lengths, mels)
    
    plt.imshow(aligns[-1].detach().cpu().numpy())

     

     

     


    Tacotron

     

    Tacotron 설계의 마지막 부분입니다.

    그동안 구현한걸 모두 넣으면 됩니다.

     

    조금 차별을 둔 점이 있다면,

    Teacher Forcing의 여부를 model을 선언할 때

    지정할 수 있게끔 한 부분입니다.

    사실 __init__ 부분에 두지 않고 Decoder부분에 True 값으로

    고정하고 해도 무방할 듯 합니다.

     

    Teacher Forcing을 사용하고 싶지 않다면,

    아래 코드블럭과 같이 model을 선언하고 사용하면 됩니다.

    model = Tacotron(hparams, teacher_forcing=False)

     

     

    논문에서는 Mel과 Spec의 Loss를 L1을 이용했다고 했으므로

    torch.nn.L1Loss() 함수를 이용해서

    다른 값들과 함께 Loss도 출력해보겠습니다.

    class Tacotron(torch.nn.Module):
        def __init__(self, Hparams, teacher_forcing=False):
            super(Tacotron, self).__init__()
            self.Hparams = Hparams
            self.teacher_forcing = teacher_forcing
            self.encoder = Encoder(self.Hparams, use_decoder=False, use_post=False)
            self.decoder = Decoder(self.Hparams, use_decoder=True, teacher_forcing=self.teacher_forcing)
            self.post_cbhg = CBHG(self.Hparams, use_post=True)
            self.linear_scaling = CustomLinear(in_features=self.Hparams.n_mels*2,
                                               out_features=self.Hparams.n_specs,
                                               activation=None,
                                               dropout=None)
    
        def forward(self, texts, mel_lengths, mels):
            B = texts.size(0)
            encoded = self.encoder(texts)
            mels, aligns = self.decoder(encoded, mel_lengths, mels)
            mels = mels.view(B, -1, self.Hparams.n_mels)
            specs = self.post_cbhg(mels)
            specs = self.linear_scaling(specs)
            return mels, specs, aligns
    dataset = KSSDataset(hparams)
    
    dataloader = torch.utils.data.DataLoader(dataset, 
                                             batch_size=4, 
                                             shuffle=True, 
                                             collate_fn=collate_fn)
    
    model = Tacotron(hparams, teacher_forcing=True)
    criterion = torch.nn.L1Loss()
    
    for step, values in enumerate(dataloader):
        texts, mels, specs, mel_lengths = values
        pred_mels, pred_specs, aligns = model(texts, mel_lengths, mels)
        mel_loss = criterion(pred_mels, mels)
        spec_loss = criterion(pred_specs, specs)
        
        print("Original Mels: {} \nPredicted Mels result: {}\
                \nOriginal Specs {} \nPredicted Specs result: {} \nAlignments result: {}\
                \nMel Loss: {} \nSpec Loss: {} \n".format(
            mels.size(), pred_mels.size(), 
            specs.size(), pred_specs.size(), aligns.size(),
            mel_loss, spec_loss))
              
        if step == 4:
            break
    
    
    ####
    Original Mels: torch.Size([4, 345, 80]) 
    Predicted Mels result: torch.Size([4, 345, 80])            
    Original Specs torch.Size([4, 345, 513]) 
    Predicted Specs result: torch.Size([4, 345, 513]) 
    Alignments result: torch.Size([4, 52, 69])            
    Mel Loss: 56.22419357299805 
    Spec Loss: 41.58668899536133 
    
    Original Mels: torch.Size([4, 350, 80]) 
    Predicted Mels result: torch.Size([4, 350, 80])            
    Original Specs torch.Size([4, 350, 513]) 
    Predicted Specs result: torch.Size([4, 350, 513]) 
    Alignments result: torch.Size([4, 50, 70])            
    Mel Loss: 41.0747184753418 
    Spec Loss: 30.709251403808594 
    
    Original Mels: torch.Size([4, 335, 80]) 
    Predicted Mels result: torch.Size([4, 335, 80])            
    Original Specs torch.Size([4, 335, 513]) 
    Predicted Specs result: torch.Size([4, 335, 513]) 
    Alignments result: torch.Size([4, 48, 67])            
    Mel Loss: 50.32085037231445 
    Spec Loss: 37.164085388183594 
    
    Original Mels: torch.Size([4, 360, 80]) 
    Predicted Mels result: torch.Size([4, 360, 80])            
    Original Specs torch.Size([4, 360, 513]) 
    Predicted Specs result: torch.Size([4, 360, 513]) 
    Alignments result: torch.Size([4, 53, 72])            
    Mel Loss: 52.56007385253906 
    Spec Loss: 38.50770568847656 
    
    Original Mels: torch.Size([4, 355, 80]) 
    Predicted Mels result: torch.Size([4, 355, 80])            
    Original Specs torch.Size([4, 355, 513]) 
    Predicted Specs result: torch.Size([4, 355, 513]) 
    Alignments result: torch.Size([4, 54, 71])            
    Mel Loss: 56.1495246887207 
    Spec Loss: 41.48576354980469

     

    Tacotron의 전체 모델 구조는 아래와 같습니다.

     

    model = Tacotron(hparams, teacher_forcing=True)
    
    print(model)
    
    
    ####
    Tacotron(
      (encoder): Encoder(
        (encoder): Sequential(
          (0): Embedding(
            (embedding_layer): Sequential(
              (0): Embedding(74, 256)
            )
          )
          (1): PreNet(
            (prenet): Sequential(
              (0): CustomLinear(
                (linear): Linear(in_features=256, out_features=256, bias=True)
                (activation): ReLU()
                (dropout): Dropout(p=0.5, inplace=False)
              )
              (1): CustomLinear(
                (linear): Linear(in_features=256, out_features=128, bias=True)
                (activation): ReLU()
                (dropout): Dropout(p=0.5, inplace=False)
              )
            )
          )
          (2): CBHG(
            (residual): Sequential(
              (0): Identity()
            )
            (conv1d_bank): ModuleList(
              (0): BatchNormConv1D(
                (conv1d): Conv1d(128, 128, kernel_size=(1,), stride=(1,), padding=(2,))
                (batchnorm1d): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
                (activation): ReLU()
              )
              (1): BatchNormConv1D(
                (conv1d): Conv1d(128, 128, kernel_size=(2,), stride=(1,), padding=(2,))
                (batchnorm1d): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
                (activation): ReLU()
              )
              (2): BatchNormConv1D(
                (conv1d): Conv1d(128, 128, kernel_size=(3,), stride=(1,), padding=(2,))
                (batchnorm1d): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
                (activation): ReLU()
              )
              (3): BatchNormConv1D(
                (conv1d): Conv1d(128, 128, kernel_size=(4,), stride=(1,), padding=(2,))
                (batchnorm1d): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
                (activation): ReLU()
              )
            )
            (conv1d_proj): Sequential(
              (0): MaxPool1d(kernel_size=2, stride=1, padding=1, dilation=2, ceil_mode=False)
              (1): BatchNormConv1D(
                (conv1d): Conv1d(512, 512, kernel_size=(3,), stride=(1,), padding=(1,))
                (batchnorm1d): BatchNorm1d(512, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
                (activation): ReLU()
              )
              (2): BatchNormConv1D(
                (conv1d): Conv1d(512, 128, kernel_size=(3,), stride=(1,), padding=(1,))
                (batchnorm1d): BatchNorm1d(128, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
              )
            )
            (pre_highway): Sequential(
              (0): CustomLinear(
                (linear): Linear(in_features=128, out_features=128, bias=True)
              )
            )
            (highway): Sequential(
              (0): Highway(
                (H): CustomLinear(
                  (linear): Linear(in_features=128, out_features=128, bias=True)
                  (activation): ReLU()
                )
                (T): CustomLinear(
                  (linear): Linear(in_features=128, out_features=128, bias=True)
                  (activation): Sigmoid()
                )
              )
              (1): Highway(
                (H): CustomLinear(
                  (linear): Linear(in_features=128, out_features=128, bias=True)
                  (activation): ReLU()
                )
                (T): CustomLinear(
                  (linear): Linear(in_features=128, out_features=128, bias=True)
                  (activation): Sigmoid()
                )
              )
              (2): Highway(
                (H): CustomLinear(
                  (linear): Linear(in_features=128, out_features=128, bias=True)
                  (activation): ReLU()
                )
                (T): CustomLinear(
                  (linear): Linear(in_features=128, out_features=128, bias=True)
                  (activation): Sigmoid()
                )
              )
              (3): Highway(
                (H): CustomLinear(
                  (linear): Linear(in_features=128, out_features=128, bias=True)
                  (activation): ReLU()
                )
                (T): CustomLinear(
                  (linear): Linear(in_features=128, out_features=128, bias=True)
                  (activation): Sigmoid()
                )
              )
            )
            (gru): Sequential(
              (0): GRU(128, 128, batch_first=True, bidirectional=True)
            )
          )
        )
      )
      (decoder): Decoder(
        (dec_prenet): PreNet(
          (prenet): Sequential(
            (0): CustomLinear(
              (linear): Linear(in_features=400, out_features=256, bias=True)
              (activation): ReLU()
              (dropout): Dropout(p=0.5, inplace=False)
            )
            (1): CustomLinear(
              (linear): Linear(in_features=256, out_features=128, bias=True)
              (activation): ReLU()
              (dropout): Dropout(p=0.5, inplace=False)
            )
          )
        )
        (attention_rnn): AttentionRNN(
          (attention_rnn): GRU(128, 256, batch_first=True)
        )
        (attention): BahdanauAttention()
        (attention_wrapper): AttentionWrapper()
        (pre_decoder_rnn): CustomLinear(
          (linear): Linear(in_features=512, out_features=256, bias=True)
        )
        (residual): Identity()
        (decoder_rnn): DecoderRNN(
          (decoder_rnn): GRU(256, 256, num_layers=2, batch_first=True)
        )
        (proj_to_mel): CustomLinear(
          (linear): Linear(in_features=256, out_features=400, bias=True)
        )
      )
      (post_cbhg): CBHG(
        (residual): Sequential(
          (0): Identity()
        )
        (conv1d_bank): ModuleList(
          (0): BatchNormConv1D(
            (conv1d): Conv1d(80, 80, kernel_size=(1,), stride=(1,), padding=(1,))
            (batchnorm1d): BatchNorm1d(80, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (activation): ReLU()
          )
          (1): BatchNormConv1D(
            (conv1d): Conv1d(80, 80, kernel_size=(2,), stride=(1,), padding=(1,))
            (batchnorm1d): BatchNorm1d(80, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (activation): ReLU()
          )
        )
        (conv1d_proj): Sequential(
          (0): MaxPool1d(kernel_size=2, stride=1, padding=1, dilation=2, ceil_mode=False)
          (1): BatchNormConv1D(
            (conv1d): Conv1d(160, 1024, kernel_size=(3,), stride=(1,), padding=(1,))
            (batchnorm1d): BatchNorm1d(1024, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
            (activation): ReLU()
          )
          (2): BatchNormConv1D(
            (conv1d): Conv1d(1024, 80, kernel_size=(3,), stride=(1,), padding=(1,))
            (batchnorm1d): BatchNorm1d(80, eps=1e-05, momentum=0.1, affine=True, track_running_stats=True)
          )
        )
        (pre_highway): Sequential(
          (0): CustomLinear(
            (linear): Linear(in_features=80, out_features=128, bias=True)
          )
        )
        (highway): Sequential(
          (0): Highway(
            (H): CustomLinear(
              (linear): Linear(in_features=128, out_features=128, bias=True)
              (activation): ReLU()
            )
            (T): CustomLinear(
              (linear): Linear(in_features=128, out_features=128, bias=True)
              (activation): Sigmoid()
            )
          )
          (1): Highway(
            (H): CustomLinear(
              (linear): Linear(in_features=128, out_features=128, bias=True)
              (activation): ReLU()
            )
            (T): CustomLinear(
              (linear): Linear(in_features=128, out_features=128, bias=True)
              (activation): Sigmoid()
            )
          )
          (2): Highway(
            (H): CustomLinear(
              (linear): Linear(in_features=128, out_features=128, bias=True)
              (activation): ReLU()
            )
            (T): CustomLinear(
              (linear): Linear(in_features=128, out_features=128, bias=True)
              (activation): Sigmoid()
            )
          )
          (3): Highway(
            (H): CustomLinear(
              (linear): Linear(in_features=128, out_features=128, bias=True)
              (activation): ReLU()
            )
            (T): CustomLinear(
              (linear): Linear(in_features=128, out_features=128, bias=True)
              (activation): Sigmoid()
            )
          )
        )
        (gru): Sequential(
          (0): GRU(128, 80, batch_first=True, bidirectional=True)
        )
      )
      (linear_scaling): CustomLinear(
        (linear): Linear(in_features=160, out_features=513, bias=True)
      )
    )

     

     

     

    설명이 난해한 부분이 있어서

    두고두고 수정해야겠네요.

     

    어쨌든 Tacotron 구현은 끝났으니

    다음 포스팅에서는 전체 데이터에 대한 전처리와 Griffin-Lim,

    train step과 iteration 부분을 다뤄보도록 하겠습니다.

     

     

    긴 글 읽어주셔서 감사합니다.

     

     

    제가 작성한 포스트에 오류가 있다면 언제든지 댓글 부탁드립니다 :)

    'Tacotron 1' 카테고리의 다른 글

    Tacotron 무지성 구현 - 7/N  (0) 2021.08.05
    Tacotron 무지성 구현 - 6/N  (0) 2021.08.02
    Tacotron 무지성 구현 - 4/N  (0) 2021.07.29
    Tacotron 무지성 구현 - 3/N  (0) 2021.07.27
    Tacotron 무지성 구현 - 2/N  (0) 2021.07.27

    댓글

Designed by Tistory.