発話音声からリアルなリップシンクを生成する技術 第1回:音素とwav2vec

発話音声からリアルなリップシンクを生成する技術 第1回:音素とwav2vec

こんにちは!

今日は当社のMotionVox でも実際に使っている「リップシンク」技術について総合的に解説してみたいとおもいます。


音声に合わせて自然な口の動きを生成するリップシンク技術は、AIアバターや3Dアニメーション制作においても重要な技術です。本記事では、最新のディープラーニング技術を活用したリップシンク学習の基礎から実装まで、技術的な観点から詳しく解説します。

1. リップシンク学習の基礎概念

1.1 問題設定

リップシンク学習とは、音声データから対応する口の動きを予測する回帰問題ととらえることができます

f: 音声特徴量(t) → 口の動きパラメータ(t)

この問題のコアは

音韻(音の特徴)と視素(視覚的な口の形)の対応関係を学習する

ことにあります。

1.2 音韻-視素マッピングの複雑性

ただし!
人間の発話における音と口の形の関係は、単純な1対1マッピングではないんです。

同じ音でも文脈で変化

「あ」の発音でも:
- 「か」の後の「あ」→ 口がやや狭めから開く
- 「ん」の後の「あ」→ 口が閉じた状態から大きく開く

調音結合(Coarticulation)

「かんぱい」の「ん」:
- 単独の「ん」→ 口を完全に閉じる
- 「ぱ」の前の「ん」→ 口を半開きで次の調音に備える

この複雑さがリアルなリップシンク実現の醍醐味であり、学習の技術的チャレンジとなっています。

2. 顔表情パラメータ表現

2.1 仮想FaceModelの仕様

本記事では、以下のような仮顔表情モデル(QualitegFaceModel=QFMとよびます)を想定して説明します

顔全体の表情パラメータは120で、うち、口回り、つまりリップシンクの制御につかうパラメータ数は26です。

# FaceModel パラメータ構成
total_params = 120  # 全表情パラメータ数
mouth_params = 26   # 口周りパラメータ数(インデックス: 94-119)

# パラメータ分類
expression_groups = {
    "eyebrow": [0, 1, 2, 3, 4, 5, 6, 7],          # 眉の動き (8個)
    "eyelid": [8, 9, 10, 11, 12, 13, 14, 15],     # まぶたの動き (8個)
    "eye_gaze": [16, 17, 18, 19],                 # 視線方向 (4個)
    "cheek": [20, 21, 22, 23, 24, 25, 26, 27],    # 頬の動き (8個)
    "nose": [28, 29, 30, 31, 32, 33],             # 鼻の動き (6個)
    "general": [34, 35, 36, 37, 38, 39, ..., 93], # その他全体表情 (60個)
    "mouth": [94, 95, 96, 97, 98, 99, ..., 119]   # 口周り (26個)
}

2.2 口周りパラメータの詳細分類

リップシンクにおいて最も重要な口周りの26パラメータは以下のようになります

mouth_parameter_details = {
    # 基本的な口の開閉・形状 (8個)
    "jaw_open": 94,           # 顎の開き
    "mouth_open": 95,         # 口の開き
    "mouth_wide": 96,         # 口の横幅
    "mouth_narrow": 97,       # 口の狭まり
    "lip_upper_up": 98,       # 上唇の上昇
    "lip_lower_down": 99,     # 下唇の下降
    "mouth_round": 100,       # 口の丸み(「う」音用)
    "mouth_stretch": 101,     # 口の横引き(「い」音用)
    
    # 左右非対称の動き (6個)
    "mouth_left": 102,        # 口の左移動
    "mouth_right": 103,       # 口の右移動
    "lip_corner_left_up": 104,    # 左口角上昇
    "lip_corner_right_up": 105,   # 右口角上昇
    "lip_corner_left_down": 106,  # 左口角下降
    "lip_corner_right_down": 107, # 右口角下降
    
    # 唇の細かい動き (8個)
    "lip_upper_left": 108,    # 上唇左部
    "lip_upper_right": 109,   # 上唇右部
    "lip_lower_left": 110,    # 下唇左部  
    "lip_lower_right": 111,   # 下唇右部
    "lip_pucker": 112,        # 唇の突出
    "lip_press": 113,         # 唇の圧迫
    "lip_bite_upper": 114,    # 上唇噛み
    "lip_bite_lower": 115,    # 下唇噛み
    
    # 特殊な調音動作 (4個)
    "tongue_tip_up": 116,     # 舌先上昇(「ら」行用)
    "tongue_back_up": 117,    # 舌奥上昇(「か」行用)
    "lip_trill": 118,         # 唇の震え(「ぶ」音用)
    "mouth_dimple": 119       # えくぼ(笑顔時)
}

2.3 音韻との対応例

では、実際の「こんにちは」という発話をするときの具体的な日本語音素と口回りのパラメータの対応をみてみましょう。

あくまでシンプル化した例ですが、「こんにちは」のそれぞれの音素を口回りのパラメータであらわすとそれぞれ以下のようになります。(実際はもっとふくざつです)

はい、要するに、この「こ」という音と、「こ」と発音したときの口の形(をあらわす制御パラメータ=口形)の対応関係を学習すればよいことになります。

というかんじで、学習のテーマとしてはシンプルなんです。対応関係も直観的です。

# 主要音素の口形パラメータ例
phoneme_mapping = {
    "/ko/": {  # 「こ」音
        "jaw_open": 0.5,      # 中程度に開く
        "mouth_open": 0.6,    # 口を開く
        "mouth_round": 0.4,   # やや丸める
        "tongue_back_up": 0.7 # 舌奥を上げる(「か」行特徴)
    },
    "/n/": {  # 「ん」音
        "jaw_open": 0.0,      # 閉じる
        "mouth_open": 0.0,    # 口を閉じる
        "lip_press": 0.9,     # 唇を圧迫
        "mouth_round": 0.0    # 丸めない
    },
    "/ni/": {  # 「に」音
        "jaw_open": 0.3,      # 少し開く
        "mouth_stretch": 0.6, # やや横に引く
        "tongue_tip_up": 0.8, # 舌先を上げる
        "lip_corner_left_up": 0.3,
        "lip_corner_right_up": 0.3,
    }
}

ただし、シンプルだからといって簡単なわけではありません。

まずは、「こ」という音をコンピューターが処理できる形式にしましょうか。

3.wav2vec がなぜ必要なのか~コンピューターが音を理解するための特徴量~

3.1 音声の「特徴量」とは何か

コンピューターは生の音を理解できない

「こんにちは」という音声をコンピューターに聞かせても、コンピューターにはただの数値の羅列にしか見えません

# 「こんにちは」の生音声データ(16kHz、1秒間)
raw_audio = [0.001, -0.002, 0.003, -0.001, 0.004, -0.002, ...]
# ↑ 16,000個の細かい数値が並んでいるだけ

# コンピューターから見ると:
「これは何の意味があるの?どこが「こ」でどこが「ん」?」

人間なら「ああ、挨拶の言葉だな」と瞬時に理解できますが、コンピューターには音の意味がわかりません。

特徴量:「意味のある情報」への変換

特徴量とは、生データを「コンピューターが理解しやすく、かつ意味のある形」に変換したものです

# 生音声 → 特徴量への変換
raw_audio = [0.001, -0.002, 0.003, ...]  # 意味不明な数値列
↓ 特徴量抽出
meaningful_features = [0.8, -0.2, 0.5, ...]  # 意味のある表現

# 特徴量の各数値が意味を持つ:
# 0.8 → 「母音っぽさ」の強さ
# -0.2 → 「高周波成分」の少なさ  
# 0.5 → 「後ろ舌音」の程度

3.2 なぜ特徴量が必要なのか~人間の例で理解しよう~

人間が「こんにちは」を理解する過程

人間の脳も実は特徴抽出をしています

# 人間の音声理解プロセス
1. 耳で音波をキャッチ
   ↓
2. 「低い音」「高い音」「継続時間」などを抽出
   ↓  
3. 「これは母音」「これは子音」と分類
   ↓
4. 「こ-ん-に-ち-は」と音韻を認識
   ↓
5. 「挨拶の言葉だ」と意味を理解

コンピューターも同じことをしたいのですが、人間のような直感的理解ができないため、数学的な特徴量が必要だよね、というわけです。

3.3 従来の方式(MFCC)の問題点

さて、せっかくなので、この音声特徴量の歴史について少しだけふれて理解を深めてみましょう。

MFCCの根本的限界

はい、いきなりMFCCという言葉がでてきましたが、従来、音声認識やリップシンクには従来の音声特徴抽出の問題点

MFCCの根本的限界

従来、音声認識やリップシンクにはMFCC(Mel-frequency Cepstral Coefficients)が広く使われていました。しかし、MFCCにはいろいろと不都合がありました。クラシックな音声信号処理を学んできた方には非常にメジャーで「メルスペクトル」とか「メル周波数ケプストラム係数(MFCCですね)」とか聞き覚えがあるでしょう。

音声認識の研究者「信号波形じゃ音声の特徴はわからん、メルスペクトルで分析するんじゃ!」とみんな口々にいっていたのも今は昔ですね。

さて、MFCCは以下のような処理フローをとります。

# MFCCの処理フロー
生音声 → フーリエ変換 → メルフィルタバンク → 対数変換 → DCT → MFCC係数

MFCCの問題点

MFCCには以下のような問題がありました。(もちろん当時はそれしかないので、問題として認識していませんでした。コロンブスのゆで卵というか、技術もサイエンスもそういうものですよね)

  1. 手工芸特徴量:人間の聴覚、聞こえ方にもとづいており、人間が恣意的に設計した固定的な特徴抽出
  2. 音韻の意味は理解しない:単純な周波数分析
  3. 文脈情報の欠如:フレーム単位の独立処理
  4. 個人差への対応不足:話者に依存しやすい傾向

具体的な失敗例

ディープラーニング以前または黎明期で音声認識を古い手法でやっていたとき以下のような失敗例があります

ケース1:調音位置の類似性を捉えられない

「か」と「が」:
- 物理的に同じ調音位置(軟口蓋)
- 口の形もほぼ同じ
- MFCCでは: 全く異なる特徴ベクトル
- 結果: モデルが「か」と「が」の類似性を学習できない

ケース2:文脈による音韻変化を無視

「ん」の発音:
- 「かん」の「ん」→ 口を完全に閉じる
- 「かんぱい」の「ん」→ 次の「ぱ」に備えて半開き
- MFCCでは: どちらも同じ「ん」として処理
- 結果: 文脈を考慮しない不自然な口の動き

ケース3:話者差への脆弱性

同じ「あ」でも:
- 男性(低い基本周波数): MFCC = [1.2, -0.5, 0.8, ...]
- 女性(高い基本周波数): MFCC = [2.1, -1.2, 1.5, ...]
- 結果: 性別が変わると全く違う口の動きを予測

そこで、登場したのが wav2vecです

その名の通り、wav(音声信号)をvec(ベクトル)にしてやろうという代物です。

3.4Wav2Vecの革新的アプローチ

自己教師あり学習による音韻理解

Wav2Vecの核心は「音韻の意味を理解する」ことです

つまり、Wav2Vec = 「音の意味を理解する特徴抽出器」です。

具体的には、MetaAI(旧Facebook AI)が開発したニューラルネットワークベースの音声特徴抽出モデルです。

技術的構成

技術的には、CNNエンコーダーで生音声から局所的な音響パターンを抽出し、その後Transformerエンコーダーで音韻の文脈的関係を理解して、最終的に768次元の特徴ベクトルを出力します。この処理により、単なる音響情報ではなく、音韻の意味的情報を含んだ豊かな表現を獲得できるようになりました。

データ駆動の特徴学習

Wav2Vecの最大の革新は、MFCCのように人間が設計した固定的な変換ではなく、ニューラルネットワークがデータから最適な特徴抽出方法を学習する点です。CNNエンコーダーは生音声から最適な音響特徴抽出方法を学習し、Transformerエンコーダーは音韻間の関係を理解して最適な文脈統合方法を獲得できます

この学習プロセスにより、MFCCでは表現できなかった複雑で非線形な音韻関係を捉えることが可能になり、また、学習データや学習目標を変更することで、特定のタスクに最適化された特徴表現を獲得することもできます

音韻的類似性の獲得

Wav2Vecは大量の音声データを使った自己教師学習により、音韻の意味的関係を理解します。マスク言語モデルと同様の手法で、音声の一部をマスクして予測するタスクを通じて、音韻間の類似性や関係性も自動的に発見できます。結果として、言語学的に類似した音素は特徴空間上でも近い位置に配置されます。「か」と「が」のような調音位置が同じ音素は高いコサイン類似度を持ち、「か」と「あ」のような全く異なる音素は低い類似度となります。この音韻的類似性の獲得により、学習効率が大幅に向上し、未知の音韻組み合わせに対する汎化性能も向上します。

文脈統合による動的表現

Wav2VecのTransformerエンコーダーは、Self-Attentionメカニズムにより音韻の前後関係を考慮した動的な特徴表現を生成します。同じ音素でも、前後の音韻文脈に応じて異なる特徴ベクトルが生成されます。

話者不変表現の学習

Wav2Vecは学習時に多様な話者の音声を使用することで、話者固有の特徴(基本周波数、フォルマント周波数など)に依存しない音韻表現を獲得します。学習目標は「異なる話者の同じ音素を同じ特徴ベクトルにマップする」ことであり、これにより話者不変性が実現されます。

結果として、男性・女性・子供・高齢者など、様々な話者が同じ音素を発音した場合でも、Wav2Vecは類似した特徴ベクトルを出力します。この話者ロバスト性により、少ない学習データでも多様な話者に対応できるリップシンクシステムの構築が可能になります。

Wav2Vecの本質的価値

Wav2Vecは単なる「新しい特徴抽出法」ではなく、「音韻の意味を理解する人工知能」として位置づけられます。MFCCが人間の考えた音の分析方法に基づいて周波数分布パターンのみを扱うのに対し、Wav2VecはAIが発見した音の理解方法により、音韻の意味的関係と文脈を深く理解します。

この技術革新により、従来のMFCCベースシステムでは達成不可能だった高精度・リアリティの高いのリップシンクのベースとなっています

次回は、wav2vecでとらえた特徴量をもとに、実際にリップシンクを学習していく手順について解説予定です。お楽しみに!

Read more

LLM推論基盤プロビジョニング講座 第3回 使用モデルの推論時消費メモリ見積もり

LLM推論基盤プロビジョニング講座 第3回 使用モデルの推論時消費メモリ見積もり

こんにちは!前回はLLMサービスへのリクエスト数見積もりについて解説しました。今回は7ステッププロセスの3番目、「使用モデルの推論時消費メモリ見積もり」について詳しく掘り下げていきます。 GPUメモリがリクエスト処理能力を決定する LLMサービス構築において、GPUが同時に処理できるリクエスト数はGPUメモリの消費量によって制約されます。 つまり、利用可能なGPUメモリがどれだけあるかによって、同時に何件のリクエストを処理できるかがほぼ決まります。 では、その具体例として、Llama3 8B(80億パラメータ)モデルをNVIDIA RTX A5000(24GB)にロードするケースを考えてみましょう。 このGPUには24GBのGPUメモリがありますが、すべてをリクエスト処理に使えるわけではありません。最初にモデル自体が一定量のメモリを消費し、残りの領域で実際のリクエスト処理を行います。 GPUメモリ消費の二大要素 GPUの消費メモリ量は主に以下の2つの要素によって決まります 1. モデルのフットプリント LLMをGPUに読み込んだときに最初に消費されるメモリ

By Qualiteg コンサルティング
システムとcondaのC++標準ライブラリ(libstdc++)のバージョン違い問題による事象と対処法解説

システムとcondaのC++標準ライブラリ(libstdc++)のバージョン違い問題による事象と対処法解説

こんにちは! 先日、dlibをつかったPythonアプリケーション(conda環境で動作する)作っていたところ、以下のようなエラーに遭遇しました。 ImportError: /home/mlu/anaconda3/envs/example_env/bin/../lib/libstdc++.so.6: version `GLIBCXX_3.4.32' not found (required by /home/mlu/anaconda3/envs/example_env/lib/python3.10/site-packages/_dlib_pybind11.cpython-310-x86_64-linux-gnu.so) 「dlib_pybind11モジュールがGLIBCXX_3.4.32を要求してるけど、みつからない!」という感じのエラーですね。

By Qualiteg プロダクト開発部
LLM推論基盤プロビジョニング講座 第2回 LLMサービスのリクエスト数を見積もる

LLM推論基盤プロビジョニング講座 第2回 LLMサービスのリクエスト数を見積もる

こんにちは! 今回はLLM推論基盤プロビジョニング講座 第2回です! STEP2 LLMサービスへのリクエスト数見積もり それでは、早速、LLM推論基盤プロビジョニングの第2ステップである「リクエスト数見積もり」の重要性と方法を解説いたします。 LLMサービスを構築する際に必要となるGPUノード数を適切に見積もるためには、まずサービスに対して想定されるリクエスト数を正確に予測する必要があります。 リクエスト数見積もりの基本的な考え方 LLMサービスへの想定リクエスト数から必要なGPUノード数を算出するプロセスは、サービス設計において非常に重要です。過小評価すればサービス品質が低下し、過大評価すれば無駄なコストが発生します。このバランスを適切に取るための基礎となるのがリクエスト数の見積もりです。 想定リクエスト数の諸元 リクエスト数を見積もるための5つの重要な要素(諸元)をみてみましょう。 1. DAU(Daily Active Users): 1日あたりの実際にサービスを利用するユーザー数です。これはサービスの規模を示す最も基本的な指標となります。 2. 1日

By Qualiteg コンサルティング
Zoom会議で肩が踊る?自動フレーミング映像安定化とAIによる性能向上の可能性

Zoom会議で肩が踊る?自動フレーミング映像安定化とAIによる性能向上の可能性

こんにちは! 本日は、自動フレーミング映像の安定化に関するアルゴリズム・ノウハウを解説いたします 第1章 問題の背景と目的 バストアップ映像を撮影する際、特にオンラインミーティングやYouTubeなどのトーク映像では、人物がうなずく、首を振るなどの自然な動作をした際に「首まわりや肩がフレーム内で上下に移動してしまう」という現象がしばしば起こります。これは、多くの場合カメラや撮影ソフトウェアが人物の「目や顔を画面中央に保とう」とする自動フレーミング機能の働きに起因します。 撮影対象の人物が頭を下げた際に、映像のフレーム全体が相対的に上方向へシフトし、その結果、本来動いていないはずの肩の部分が映像内で持ち上がっているように見えてしまう現象です。 本稿では、この問題を撮影後の後処理(ポストプロセッシング)のみを用いて、高速、高い精度かつロバストに解決する手法をご紹介します。 前半では、従来のCV(コンピュータービジョン)の手法を使い高速に処理する方法をご紹介します。後半では、AIを使用してより安定性の高い性能を実現する方法について考察します。 第2章 古典手法による肩の上下

By Qualiteg 研究部