---
title: 【Pythonで学ぶ音声認識】第7章：End-to-Endモデルによる連続音声認識（7.4節）
tags: 
author: [京都大学人工知能研究会KaiRA](https://www.docswell.com/user/kyoto-kaira)
site: [Docswell](https://www.docswell.com/)
thumbnail: https://bcdn.docswell.com/page/GEW353RKE2.jpg?width=480
description: Pythonで学ぶ音声認識の輪読会第10回の発表スライドです。 2023年12月21日(木) 18:30～
published: December 28, 23
canonical: https://www.docswell.com/s/kyoto-kaira/5M18X6-2023-12-28-143451
---
# Page. 1

![Page Image](https://bcdn.docswell.com/page/GEW353RKE2.jpg)

音声認識
7章 4節
CTCの実装
京都大学工学部物理工学科2回生
平塚謙良
0


# Page. 2

![Page Image](https://bcdn.docswell.com/page/47ZVNVRNE3.jpg)

CTC(Connectionist Temporal Classification)の復習
・RNNを用いて，直接出力テキストを学習する
→学習にHMM状態ラベルが要らない
・前向き-後ろ向きアルゴリズムの導入
→誤差逆伝播法が適用可能に
前向き・後ろ向きアルゴリズム
https://tech.zealscott.com/cmu-dl/L16%20Connectionist%20Temporal%20Classification.html
・ブランクの導入
→曖昧な区間への無理なラベリングを防ぐ
→同一トークンが連続するテキストを認識
ブランクの導入
https://ratsgo.github.io/speechbook/docs/neuralam/ctc
1


# Page. 3

![Page Image](https://bcdn.docswell.com/page/YJ6585R97V.jpg)

CTCの実装
2


# Page. 4

![Page Image](https://bcdn.docswell.com/page/GJ5Y9Y8DJ4.jpg)

CTCの実装
my_model.pyとencoder.pyに分けて実装
my_model.py
encoder.py
● CTCのモデル構造全体を定義
● エンコーダを実装
● エンコーダの作成
● 射影層(線形層)による次元削減
● 出力層の作成
● サブサンプリング(間引き)
● LeCunの初期化(initialize.pyに実装)
class MyCTCModel(nn.Module):
def __init__(…):
super(MyCTCModel, self).__init__()
self.encoder = Encoder(…) # エンコーダーを作成
self.out = nn.Linear(…) # 出力層を作成
lecun_initialization(self) # LeCunの初期化を実行
# ネットワーク計算の関数
def forward(self,input_sequence,input_lengths):
enc_out, enc_lengths = self.encoder(input_sequence,
input_lengths)
output = self.out(enc_out)
return output, enc_lengths
3


# Page. 5

![Page Image](https://bcdn.docswell.com/page/LE35Y5GQJ5.jpg)

CTCの実装(encoder.py)
class Encoder(nn.Module):
def __init__(略):
# RNN層の定義
rnn = []
for n in range(self.num_layers):
# rnn_type がGRUならGRUを，それ以外ならLSTMを用いる
if rnn_type == &#039;GRU&#039;:
rnn.append(nn.GRU(…))
else:
rnn.append(nn.LSTM(…))
self.rnn = nn.ModuleList(rnn) # 標準のリスト型からModuleListに変換する
# 射影層の定義
proj = []
for n in range(self.num_layers):
proj.append(nn.Linear(…))
self.proj = nn.ModuleList(proj) # RNN層と同様，ModuleListに変換する
def forward(self, sequence, lengths):
for n in range(self.num_layers):
output, (h, c) = self.rnn[n](rnn_input) # RNN層に入力する
# sub sampling (間引き)の実行
if sub &gt; 1:
output = output[:, ::sub]
# フレーム数を更新する
output_lengths = (output_lengths+1) // sub
output = self.proj[n](output) # Projection層に入力する
return output, output_lengths
※左のコードは色々省いているので正
確ではない
RNNはGRU, LSTMのどちらを使う
か選べるようになっており，どちらも
Pytorchに実装されたものを使ってい
る。
射影層は入力次元数の半分の出力次
元数を持つ。
for文を用いてRNN層と射影層を交
互に実行する。
事前に設定しておいた数値に従いフ
レームを間引く。
4


# Page. 6

![Page Image](https://bcdn.docswell.com/page/8EDD5D1WEG.jpg)

全体の実装
5


# Page. 7

![Page Image](https://bcdn.docswell.com/page/V7PXGXQXE8.jpg)

全体の実装
01_get_token.py
ニューラルネットワークで扱うために，トークンを番号へ変換
●
02_train_ctc.py
my_model.pyに実装したCTCを学習
●
03_decode_ctc.py
学習済みモデルを用いて評価データをデコーディング
●
04_scoring.py
レーベンシュタイン距離によって認識結果を評価
●
6


# Page. 8

![Page Image](https://bcdn.docswell.com/page/2JVL6L537Q.jpg)

全体の実装(02_train_ctc.py)
model = MyCTCModel(…)
optimizer = optim.Adadelta(model.parameters(),…)
# 訓練/開発データのデータセットを作成する
train_dataset = SequenceDataset(…)
# 訓練データのDataLoaderを呼び出す
train_loader = DataLoader(train_dataset,…)
# CTC損失関数を呼び出す．blankは0番目と定義する．
criterion = nn.CTCLoss(blank=0, reduction=&#039;sum’)
# 勾配をリセット
optimizer.zero_grad()
# モデルの出力を計算(フォワード処理)
outputs, out_lens = model(features, feat_lens)
# CTC損失関数を使う場合は，log_softmaxを通す必要がある
outputs = F.log_softmax(outputs, dim=2)
# 損失値を計算する．
loss = criterion(outputs.transpose(0, 1),…)
# 勾配を計算する
loss.backward()
# Cliping Gradient により勾配が閾値以下になるよう調整する
torch.nn.utils.clip_grad_norm_(model.parameters(),…)
# オプティマイザにより，パラメータを更新する
optimizer.step()
準備部分
MyCTCModelはmy_model.pyに実装した物。
optimizerにはAdadeltaを，損失関数には
Pytorchのtorch.nn.CTCLossを用いる。
学習部分
対数事後確率を計算するために
torch.nn.functional.log_softmax()を使っ
ている。
また，torch.nn.utils.clip_grad_norm_()に
よりGradient Clippingを行っている。
これにより勾配爆発を防ぐことができる。
7


# Page. 9

![Page Image](https://bcdn.docswell.com/page/5EGXNX6YJL.jpg)

全体の実装(03_decode_ctc.py)
def ctc_simple_decode(int_vector, token_list):
output = [] # 出力文字列
prev_token = -1 # 一つ前フレームの文字番号
for n in int_vector:
if n != prev_token: # 1.前フレームと同じトークンではない
if n != 0: # 2. かつ，blank(番号=0)ではない
output.append(token_list[n])
# 前フレームのトークンを更新
prev_token = n
return output
# モデルの出力を計算(フォワード処理)
outputs, out_lens = model(features, feat_lens)
for n in range(outputs.size(0)): # バッチ内の1発話ごとに処理
# 要素番号を取得する
idx = torch.nonzero(indices==n, as_tuple=False).view(-1)[0]
# 各フレームのmax値をたどる Best path decoding を行う
_, hyp_per_frame = torch.max(outputs[idx], 1)
# numpy.array型に変換
hyp_per_frame = hyp_per_frame.cpu().numpy()
# 認識結果の文字列を取得
hypothesis = ctc_simple_decode(hyp_per_frame, token_list)
CTC出力をトークン列に変換
ブランクを考慮して変換を行う。
認識部分
torch.max()でフレーム毎に最も確率の高い
トークンを取り出している。
先ほど定義したctc_simple_decode()によ
り連続する同一トークンとブランクを削除
して認識結果を出力する。
8


# Page. 10

![Page Image](https://bcdn.docswell.com/page/4JQ8Q8467P.jpg)

全体の実装(レーベンシュタイン距離)
認識誤りを「置換誤り」，「削除誤り」，「挿入誤り」の三つに
分類してカウントする。このカウントの合計をレーベンシュタイ
ン距離という。
文字誤り率は以下のように定義される。
置換誤り数+削除誤り数+挿入誤り数
文字誤り率 =
正解ラベルの文字数
置換誤り
https://mieruca-ai.com/ai/levenshtein_jaro-winkler_distance/
削除誤り
挿入誤り
9


# Page. 11

![Page Image](https://bcdn.docswell.com/page/K742N2QMJ1.jpg)

学習結果
10


# Page. 12

![Page Image](https://bcdn.docswell.com/page/LJ1M5MPYEG.jpg)

学習結果
RNNのアルゴリズムはGRUとLSTM，データセットはsmall(1000)
とlarge(4500)で比較。
Small
GRU
Large
学習時間(分):1.5
学習時間(分):8.5
文字誤り率(%):7.31 文字誤り率(%):3.34
LSTM 学習時間(分):2.5
学習時間(分):10
文字誤り率(%):8.13 文字誤り率(%):3.60
Small_GRU 損失
Small_LSTM 損失
GRUを用いてLargeサイズ
のデータセットで学習した
モデルが最も高い性能を示
した。
Large_GRU 損失
Large_LSTM 損失
11


# Page. 13

![Page Image](https://bcdn.docswell.com/page/GJW35321J2.jpg)

学習結果
最も性能のよかったモデルの音声認識結果を見ると，そこそこ精
度よく認識できている。
ID: BASIC5000_0041
#ERROR (#SUB #DEL #INS): 0 (0 0 0)
REF: し が い せ ん わ ひ ふ が ん を ひ き お こ す こ と が あ る
HYP: し が い せ ん わ ひ ふ が ん を ひ き お こ す こ と が あ る
ID: BASIC5000_0042
#ERROR (#SUB #DEL #INS): 0 (0 0 0)
REF: き が つ く と に げ ば わ ど こ に も な か っ た
HYP: き が つ く と に げ ば わ ど こ に も な か っ た
ID: BASIC5000_0043
#ERROR (#SUB #DEL #INS): 2 (1 1 0)
REF: さ ん ば ん め の そ し て も っ と も じ ゅ ー よ ー な か ん が え わ さ い に ゅ ー と
ゆーことである
HYP: さ ん ば ん め の そ し て も っ と も じ ゅ ー よ ー な か ん が え わ さ い に ゅ ー と
よことである
↑
置換誤りと削除誤りを1つずつしている
12


# Page. 14

![Page Image](https://bcdn.docswell.com/page/4EZVNVDXJ3.jpg)

まとめ
まとめ１
まとめ２
まとめ３
CTCを用いることでEnd-to-Endの音声認識モデルが作れる
レーベンシュタイン距離により文字誤り率を計算できる
LSTMと比較してGRUの方が学習時間は短く，認識精度も同等もしくはそれより高
い
13


# Page. 15

![Page Image](https://bcdn.docswell.com/page/Y765851PEV.jpg)

14


