axinc-ai / ailia-models

The collection of pre-trained, state-of-the-art AI models for ailia SDK
2.04k stars 325 forks source link

Implement soundchoice-g2p #1500

Closed ooe1123 closed 4 months ago

ooe1123 commented 4 months ago

https://github.com/axinc-ai/ailia-models/issues/1479 のPRです

kyakuno commented 4 months ago

モデルアップロード済み。 https://storage.googleapis.com/ailia-models/soundchoice-g2p/rnn_beam_searcher.onnx など

kyakuno commented 4 months ago

g2p_enのトークンと互換性はありそう。

Input : To be or not to be, that is the question g2p_en : ['T', 'UW1', 'B', 'IY1', 'AO1', 'R', 'N', 'AA1', 'T', 'T', 'UW1', 'B', 'IY1', ',', 'DH', 'AE1', 'T', 'IH1', 'Z', 'DH', 'AH0', 'K', 'W', 'EH1', 'S', 'CH', 'AH0', 'N'] soundchoice-g2p : T-UW- -B-IY- -AO-R- -N-AA-T- -T-UW- -B-IY- -DH-AE-T- -IH-Z- -DH-AH- -K-W-EH-S-CH-AH-N

kyakuno commented 4 months ago

@ooe1123 実装、ありがとうございます!ベンチマークを使用するすると、beam searchでエラーが発生するようでして、何かコンテキストが残っているかもしれません。お手数ですが、ご確認いただけると嬉しいです。

kyakuno@mbakk soundchoice-g2p % python3 soundchoice-g2p.py -b
 INFO arg_utils.py (13) : Start!
 INFO arg_utils.py (163) : env_id: 2
 INFO arg_utils.py (166) : MPSDNN-Apple M2
 INFO model_utils.py (89) : ONNX file and Prototxt file are prepared!
 INFO model_utils.py (89) : ONNX file and Prototxt file are prepared!
 INFO model_utils.py (89) : ONNX file and Prototxt file are prepared!
 INFO license.py (81) : ailiaへようこそ。ailia SDKは商用ライブラリです。特定の条件下では、無償使用いただけますが、原則として有償ソフトウェアです。詳細は https://ailia.ai/license/ を参照してください。
 INFO soundchoice-g2p.py (311) : Input text: To be or not to be, that is the question
 INFO soundchoice-g2p.py (314) : Start inference...
 INFO soundchoice-g2p.py (316) : BENCHMARK mode
 INFO soundchoice-g2p.py (325) :    ailia processing estimation time 913 ms
Traceback (most recent call last):
  File "/Users/kyakuno/Desktop/ailia/ailia-models-ax/natural_language_processing/soundchoice-g2p/soundchoice-g2p.py", line 374, in <module>
    main()
  File "/Users/kyakuno/Desktop/ailia/ailia-models-ax/natural_language_processing/soundchoice-g2p/soundchoice-g2p.py", line 370, in main
    recognize_from_text(models)
  File "/Users/kyakuno/Desktop/ailia/ailia-models-ax/natural_language_processing/soundchoice-g2p/soundchoice-g2p.py", line 320, in recognize_from_text
    phonemes = predict(models, input_text)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/kyakuno/Desktop/ailia/ailia-models-ax/natural_language_processing/soundchoice-g2p/soundchoice-g2p.py", line 303, in predict
    phonemes = compute_outputs(net, p_seq, encoder_outputs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/kyakuno/Desktop/ailia/ailia-models-ax/natural_language_processing/soundchoice-g2p/soundchoice-g2p.py", line 272, in compute_outputs
    hyps, scores, *_ = S2SBeamSearcher(net, args.onnx).forward(encoder_outputs)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/kyakuno/Desktop/ailia/ailia-models-ax/natural_language_processing/soundchoice-g2p/beam_searcher.py", line 994, in forward
    ) = self.search_step(
        ^^^^^^^^^^^^^^^^^
  File "/Users/kyakuno/Desktop/ailia/ailia-models-ax/natural_language_processing/soundchoice-g2p/beam_searcher.py", line 869, in search_step
    ) = self._scorer_step(
        ^^^^^^^^^^^^^^^^^^
  File "/Users/kyakuno/Desktop/ailia/ailia-models-ax/natural_language_processing/soundchoice-g2p/beam_searcher.py", line 378, in _scorer_step
    score, new_memory["ctc"] = ctc_score(inp_tokens, memory["ctc"], attn)
                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/Users/kyakuno/Desktop/ailia/ailia-models-ax/natural_language_processing/soundchoice-g2p/beam_searcher.py", line 145, in ctc_score
    psi_init = np.expand_dims(r[start - 1, 0], axis=0)
                              ~^^^^^^^^^^^^^^
IndexError: index 40 is out of bounds for axis 0 with size 40
kyakuno commented 4 months ago

To be or not to be, that is the questionの推論時間。

BLAS : 495 ms MPS : 634 ms

kyakuno commented 4 months ago

rnn_beam_searcher.onnxがseq2seqのdecoderに相当する。S2SBeamSearcherの中でbeam_size=16が定義されている。

kyakuno commented 4 months ago

S2SBeamSearcherのbeam_size=1にした上で、_update_reset_memoryのhsの16も1にすると、greedy searchにできる。

Beam Search 16 : 495ms T-UW- -B-IY- -AO-R- -N-AA-T- -T-UW- -B-IY- -DH-AE-T- -IH-Z- -DH-AH- -K-W-EH-S-CH-AH-N Greedy Search : 208ms T-UW- -B-IY- -AO-R- -N-AA-T- -T-UW- -B-IY- -DH-AE-T- -IH-Z- -DH-AH- -K-W-EH-S-CH-AH-N

kyakuno commented 4 months ago

rnnのアーキテクチャ https://arxiv.org/pdf/2207.13703

IMG_2843

kyakuno commented 4 months ago

tokenizerは普通のbert。 token_type_idsについて。今回のケースでは全て0になりそう。 https://qiita.com/Dash400air/items/a616ef8d088e003dfd4c encode_plusはattention maskを取得するために使う。 https://zenn.dev/hellorusk/articles/7fd588cae5b173

ooe1123 commented 4 months ago

@ooe1123 実装、ありがとうございます!ベンチマークを使用するすると、beam searchでエラーが発生するようでして、何かコンテキストが残っているかもしれません。お手数ですが、ご確認いただけると嬉しいです。

@kyakuno 問題の修正を行いました!

kyakuno commented 4 months ago

動作確認できました!ご対応、ありがとうございました。

kyakuno commented 4 months ago

@ooe1123 embeddingsの下記のコードが少し不思議でして、hidden_statesは(n_token, 1, n_token, 768)で、トークン列の末尾4つを縮退するようなコードになっていまして、こちらのコードのtorchの元コードを教えていただくことは可能でしょうか?

    # get_hidden_states
    layers = [-4, -3, -2, -1]
    output = np.sum(hidden_states[layers], axis=0)
    output = np.squeeze(output)
    output = output[token_ids_word]
ooe1123 commented 4 months ago

@kyakuno

こちらのコードのtorchの元コード

オリジナルコードでの当該部分は以下になります。 https://github.com/speechbrain/speechbrain/blob/develop/speechbrain/wordemb/transformer.py#L247-L253

kyakuno commented 4 months ago

@ooe1123 ありがとうございます!現在、emb.onnxについて、output shapeが(len, 1, len, 768)になっているのですが、(4, 1, len, 768)か、(n_layers, 1, len, 768)が正しそうな気がするのですが、いかがでしょうか?トークン長が、4より小さいか、bertのレイヤー数より大きくなると、推論エラーになりそうな気がしました。

スクリーンショット 2024-06-24 11 17 05

ooe1123 commented 4 months ago

@kyakuno

ご確認ありがとうございます。 input_idsの長さを変えて数パターンみてみたところ、 hidden_stateは、(13, 1, len, 768) の形状となるのが正しいようです。

soundchoice-g2p_emb.onnx を再エクスポートしてご提供致します。

kyakuno commented 4 months ago

お手数おかけします。よろしくお願いします!

kyakuno commented 4 months ago

後、追加ですみません。

    token_ids_word = np.array(
        [idx for idx, word_id in enumerate(encoded.word_ids()) if word_id is not None],
    )

なのですが、現状の文字列の場合、サブワード分割されないので問題ないのですが、"To be or not to be, that is the questionary"を入力として、questionaryがサブワード分割されるようにした場合、

word_ids [None, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, None]
token_ids_word [ 1  2  3  4  5  6  7  8  9 10 11 12]

となるようでして、本来的には、最後の[10, 10]がまとめられて、

token_ids_word [ 1  2  3  4  5  6  7  8  9 10 11]

となるのが正しい気がするのですが、いかがでしょうか?

この後段で、expand_to_charsをしており、この中のwordsのshapeのword数と、embeddingのshapeのword数が一致しないと、word単位のembeddingをchar単位のembeddingに変換できないような気がしました。

雑なコードですが、下記のような論理なのかなと思いました。

    max_word_id = 0
    for idx in range(0, len(encoded.word_ids())):
        if encoded.word_ids()[idx] == None:
            continue
        max_word_id = encoded.word_ids()[idx]
    token_ids_word = []
    for idx in range(0, max_word_id + 1):
        token_ids_word.append(np.where(np.array(encoded.word_ids()) == idx)[0][0])
    token_ids_word = np.array(token_ids_word)
kyakuno commented 4 months ago

"To be or not to be, that is the questionary"のように","が存在する場合、grapheme_pipelineで","が削除されるものの、BERTの Tokenizerでは","を単語として扱うため、token_ids_wordの数と、expand_to_charsの中のseqを分割した数も合わなくなる気がしますね。

ooe1123 commented 4 months ago

↑の議論はすいません、専門的な知識がなくて、 この処理が本質的に何をやっているのか理解できていないため、私には是非が分からないです。

オリジナルだと embeddingsの処理はここで、 https://github.com/speechbrain/speechbrain/blob/develop/speechbrain/wordemb/transformer.py#L196-L203 expand_to_charsの処理はこちらになります。 https://github.com/speechbrain/speechbrain/blob/develop/speechbrain/wordemb/util.py#L11

kyakuno commented 4 months ago

承知しました。公式でテストしてみたところ、推測が正しかったので、私の方で修正します。

text = "To be or not to be, that is the question"

emb.shape torch.Size([1, 11, 768])
seq tensor([[ 0, 22, 17, 30,  4,  7, 30, 17, 20, 30, 16, 17, 22, 30, 22, 17, 30,  4,
          7, 30, 22, 10,  3, 22, 30, 11, 21, 30, 22, 10,  7, 30, 19, 23,  7, 21,
         22, 11, 17, 16]])
seq.shape torch.Size([1, 40])
words tensor([[0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 6,
         7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9]])
words.shape torch.Size([1, 40])
text = "To be or not to be, that is the questionary"

emb.shape torch.Size([1, 12, 768])
seq tensor([[ 0, 22, 17, 30,  4,  7, 30, 17, 20, 30, 16, 17, 22, 30, 22, 17, 30,  4,
          7, 30, 22, 10,  3, 22, 30, 11, 21, 30, 22, 10,  7, 30, 19, 23,  7, 21,
         22, 11, 17, 16,  3, 20, 27]])
seq.shape torch.Size([1, 43])
words tensor([[0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 6, 6,
         7, 7, 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9]])
kyakuno commented 4 months ago

すみません、やっぱり、公式のコードの段階で問題がありそうなので、公式に聞いてみます。 https://github.com/speechbrain/speechbrain/issues/2580

kyakuno commented 4 months ago

公式に乗っ取ったサンプルとしては現在のもので問題なさそうです。