Open GENZITSU opened 1 year ago
キャラクターの発話と唇の動きを一貫性のある自然な形でアニメーション生成するリップシンク技術を機械学習により高精度化した事例の紹介。
発話と唇の動きに一貫性を持たせたいのは山々だが、手作業で修正していくには無理がある。。
そのため、多くの企業が現在もリップシンクアニメーションの自動生成技術開発に取り組んでいる。スクウェア・エニックスでは従来、“音素”をベースにした“HappySadFace”(以下、HSF)というリップシンクアニメーション作成プログラムを使用していた。しかしこのシステムにも問題がいくつかあり、それを解消すべく新たに“Lip-Sync ML”という、“機械学習”をベースとしたシステムを開発したという。
キャラクターの発話を音素に分解して、各音素を唇の動きが事前に定義された特定音素の重ね合わせで表現することで最終的な動きを求める。
なかなかいい手法なのだが、アドリブや呼吸音に対する対応が難しかったらしい。
『FF7 リメイク』のヒロイン・エアリスのリップシンクアニメーション動画を再生したが、明らかにセリフがない部分で、大きく口が動いていた。言葉を発する直前の呼吸音に音素として反応し、アニメーションが生成されてしまったのだ。
これを解決したのLip Sync ML
上述の問題を解決し、より自然な唇モーションを生成することが可能な機械学習ツール。
下記のように訓練データにないキャラクターや人間ではないキャラクターにたいしても器官点さえ定義できれば適用可能。
多数のデータが2秒程度の短いものばかりで、長いデータとの差異が生じかねないという問題もあったが、近似データを連結することでこの問題は解決した。
音声の速度や異なるキャラクターによるピッチの変化に対し、ロバスト性(外部の要因に影響されにくい性質)を向上させるためにいくつかのデータ拡張を実施。ランダムで速度やピッチを変化させたクリップを、訓練データに混ぜた
アーキテクチャーは以下
データの流れ
音源をログメルスペクトログラムに変換
音声モデルは生のオーディオ信号をモノラルに変換し、19.2kHzにリサンプリングする。一般的なレートではないのは、アニメーションフレームと音声サンプルとの適切な同期を確実にするため
音声にマッチしたアニメーション生成というタスクでルールベースによる処理をMLモデルに正当に進化させている良い事例。
一見事前学習いるのか?というようなタスク設定ではあったが、オープンドメインデータによる事前学習で性能が上がっているのは普段の業務で意識しておきたいポイントかもしれない。
『FF7 リメイク』進化したリップシンク技術を実例付きで紹介。テキスト入力不要でアニメーション生成が可能! 機械学習により別次元のクオリティーへ【CEDEC2022】
時系列データを扱う際によく扱う処理が一覧になってまとまっている。
参考になったものだけ抜粋
一旦、秒で取得して60で割っている
# https://www.eureka-moments-blog.com/entry/2022/09/13/221213
data_df["dif_min"] = data_df["date"].diff().dt.total_seconds() / 60
data_df["dif_min"] = data_df["dif_min"].fillna(0)
print(data_df["dif_min"].head())
対象日時も一旦datetime型にしているのがポイント
# from https://www.eureka-moments-blog.com/entry/2022/09/13/221213
event_df["date"] = pd.to_datetime(event_df["date"], format="%Y-%m-%d %H:%M:%S")
base_time = "2016-01-11 17:00:00"
event_df["dif_min"] = event_df["date"] - dt.datetime.strptime(base_time, "%Y-%m-%d %H:%M:%S")
event_df["dif_min"] = event_df["dif_min"].dt.total_seconds() / 60
メモ
pandasのdf.bar()を用いていい感じなグラフを生成するためのtips集
参考になったものだけ抜粋
# from https://www.shanelynn.ie/bar-plots-in-python-using-pandas-dataframes/
plotdata.plot(kind='bar', stacked=True)
plt.title("Total Pie Consumption")
plt.xlabel("Family Member")
plt.ylabel("Pies Consumed")
# from https://www.shanelynn.ie/bar-plots-in-python-using-pandas-dataframes/
plotdata[["pies_2020", "pies_2018", "pies_2019"]].plot(kind="bar", stacked=True)
plt.title("Mince Pie Consumption Totals")
plt.xlabel("Family Member")
plt.ylabel("Pies Consumed")
# from https://www.shanelynn.ie/bar-plots-in-python-using-pandas-dataframes/
plotdata.reset_index().plot(
x="index", y=["pies_2018", "pies_2019"], kind="bar"
)
plt.title("Mince Pie Consumption 18/19")
plt.xlabel("Family Member")
plt.ylabel("Pies Consumed")
# from https://www.shanelynn.ie/bar-plots-in-python-using-pandas-dataframes/
colours = {"male": "#273c75", "female": "#44bd32"}
plotdata['pies'].plot(
kind="bar",
color=plotdata['gender'].replace(colours)
)
n_colsで行数を変えることが可能
# from https://www.shanelynn.ie/bar-plots-in-python-using-pandas-dataframes/
# Plot and control the legend position, layout, and title with .legend(...)
plotdata[["pies_2020", "pies_2018", "pies_2019"]].plot(
kind="bar", stacked=True
).legend(
loc='upper center', ncol=3, title="Year of Eating"
)
plt.title("Mince Pie Consumption Totals")
plt.xlabel("Family Member")
plt.ylabel("Pies Consumed")
df.barで積み上げ棒グラフ作れるの知らなかった。
不確実性の高い機械学習プロジェクトを円滑に進め最大限の成果を得るためのマネジメント戦略について述べられたスライド。
DeNAで長年MLプロジェクトをマネジメントされているyurfuwaさんが書かれた力作。
yurfuwaさんが書いたAI Project Management Anti Patternも良作なので是非読まれたい。
正直全スライド必読ではあるのだが、キリがないので特に重要なポイントのみメモ
- 機械学習プロジェクトが増えていく中で、PoCの終了条件とMVPの境界前提が確立し なままプロジェクト進行するケースが存在する
- PoCが不完全なままMVPを進行した場合、前提条件の破綻とコスト増大リスクを抱え ることになる
- チームのレベル差にはバラツキがあり、PjM/PdMも例外ではない。ある種のスーパ ーマンの成功体験のみを生存バイアスとして活用することは依存であって、持続的か つスケーラブルな組織を目指さねばならない
多くのML PRJのPoCは終了条件が不明瞭 ないしは 何がなんでもPoCを終わらせることが宿命づけられている印象
実際はPoCをちゃんとやらないことで後の工程の工数を漠増させる結果になりがち。
また、ML PRJの属人性は他のシステム開発よりも高いので、意識的にサステナブルな組織を目指す必要がある。
- アセスメント、PoC、MVPのフェーズ終了条件/境界を定め、フレームワーク(AI Project Management Flow)にすることで個々に目指すべきゴールが明確になり、イテレーション回数を増やし、結果的にプロダクト価値の最大化と案件ごとの品質差異を最小化する
- 合わせて多面的なビルドトラップレビューを行い、組織長のような管理者ではなく、自律的なチーム同士がプロジェクトのゴール確認・健全性のチェック・品質の均一化・そしてそれらのナレッジトランスファーを行うことで、効率的にチームが提供する機械学習プロダクトのアウトカムを最大化しながら、組織のスケーラビリティを高める
ここの工程をアジャイルで進めるのは良いが、一工程ずつの達成条件/撤退条件を作成してフレームワーク化する
また、チームメンバー同士のレビューを通してナレッジシェアを行うことで、ML実装に係るリスクの最小化と組織の成長を目指す必要がある。
とくに注意しなければいけない点は下記
- 先方ビジネス課題のヒアリング
- 渉外 or BizDev + データサイエンティスト or アナリスト
- ✗UXデザイナーやエンジニアは随分後から適宜合流
- △Proof of Concept で検証サイクルを回しながら本番運用化を探る
- PoCと運用のシステムアーキテクチャは全く異なる可能性が考慮されていない
- ✗ ステークホルダーとその担当者によって各レベルの理解がまちまち
AI PRJ における PoCとはなにか
- AIプロジェクトの概念では AI/DSモデル開発のフィージビリティとする
- 基本的に仮説検討した、「アルゴリズムの実証」を行う
- サービス向けのインターフェース開発及びプロトタイプ開発を伴わない
AI PRJにおけるMVPとはなにか
- PoCで実装された開発モデルを「サービスに組み込み検証」する「最小プロトタイプ開発」
- 当然成果物としてプロダクト開発を伴う
- ただし、UIはあってもなくても良い
- PoC自体もMVPを意識した上でモデル開発に注力すべきなので、PoCでモデル開発に時限を区切り、MVPでエンドユーザーを意識した最小のプロダクト開発を行うというのがポイント
- 勿論PoCのアルゴリズムが実証されても、MVPの体験検証でアセスメント (課題設定)の引き直しからやり直す事もありえる。
- 重要なことは、この単位をいかに最小構成に定義しイテーレーションを繰り返すことができるのか、にかかっている。
よくある罠: プロダクトとしての要件をおざなりにしてアルゴリズムのフィージビリティにリソースを投下しすぎる
いつのまにかPoCの流れでPdMを介せずにMVP要件をヒアリングを実施し場当たり的にリソースを調整
= いつのまにか非専門領域のインターフェース実装を任される
時間的・予算的にPdMやUXデザイナー/SEWを入れていない
- 一旦AIエンジニアがMVP開発まで良かれと思ってやってしまう
- ✗ MVPとしてのPdMやUXの観点がまるっと抜けてしまう
ヒアリングとその場の判断でMVPに必要な要件をなんとなく確定している
- ✗ その要件が本当に顧客にとって必要なものかフィージビリティがとれていない
- ✗ アルゴリズム開発が先行し、UXやユースケースの深掘りをはじめ、MVP開発は後手に
PoCとMVPとプロダクト開発の境界をはっきりさせる
- 機械学習のPoCはアルゴリズムの提案と解法レポートに集中する。
- アルゴリズム検証PoCでMVPの要件をコミットをしてはならない
- PoCフェーズを終了しMVPはMVPで体制から「仕切り直す」
- PoCとMVP,本開発それぞれのゴールを細分化したコンセンサスが必要
これらを実施することでステークホルダー全員がPRJの先を見れるようになる。
以下のようなマネジメントフローが理想的とのこと
- レベル及びフェーズで各プロジェクトをnotionで管理
- レベル単位のサイクルで以下を実施
- 「体制見直し」,「全体キックオフ」, 「振り返りと評価」
これらを実施することで、得られるメリット
各レベルやフェーズで実施すべきこと
スピード先行によってできなかったチーム開発品質を維持する施策実施
- リファクタリング,テスト,ドキュメンテーションなどの運用品質向上
- 特に体制見直しに合わせた新メンバーのオンボーディングで効果を発揮
- 新メンバーが旧メンバーとペアプロやSyncをしながら進めるのが◎
- プロジェクトマネジメントツール、手法の見直し
- 新メンバーが入ることを踏まえた前フェーズからのドキュメント整備
- 全体KPTを必ず実施し、チーム開発課題を洗い出す
- 前フェーズで課題を残したまま新メンバーを入れて新フェーズに入ることを許さない
特にこういったことは意識的にゆとりを作らないと後回しになりがちなので、自動的にフェーズ境界で時間とリソースのゆとりが発生する上記のマネジメントフローと相性がいいかもしれない。
ここで発生させたゆとりを別PRJに食いつぶされることもよくあるが笑
ビルドトラップとは
機能の開発とリリースに集中してしまい、顧客の本当の課題、プロダクトの本当の価値がおざなりになってしまう状況
これに陥らないためにどんなレビューを実施するべきか
- そのフェーズにおける、ゴールを確認
- PjM/PdMがプロジェクトやAIプロダクトに対する短期および中長期のゴールを答えられないようでは話にならない。 -そもそもプロジェクトの目的とアウトカムが定まっているか。
- PoCからMVPを経てアウトカムがブレていないか。
このゴールの明確化と妥当性を確認するだけでも、時間をかける価値があるとのこと
くわえてやれるといいこと
- Role/リソースの短期・中長期妥当性
- ○ 現フェーズで非専門領域に対するフィージビリティを門外漢がやっていないか
- ○ 現フェーズの見通しにより、次フェーズの準備で行うべきタスクが整理/実施されているか
- 法務セキュリティ関連
- AIガバナンス観点
- アウトカムに立ち返ることが最も重要
- 適切なタイミングで適切なプロを入れる仕組みとコンセンサスをする
- 駄目だったら撤退できる余裕を作る
- 焦らないで地道にやれる仕組みを作る
- スピード感とは、「感」ではなく「不確実性を減らし先を見通す力」と、その実行力である
タイミングを適切に分ける、タイミングごとに振り返りや調整を行う、イテレーションを回す余裕を作るなど
どれも大事だけど実行が難しい...
後半に書いてあった心強い言葉が強く胸に響いた
- 多角事業組織は、一つの成功体験がいくつもの別の事業での成功に横展開できることが最大の強みである。実績を積み上げ、その実績を別の課題・ユースケースに活かすことも、持続的な組織戦略となる
- ○ 単なるプロジェクトの成功秘話にとどまらず、アルゴリズムの技術的課題から性能改善からププロダクトのアウトカムに繋げる具体施策までチームが持って変えるナレッジに底は無い
実行するのが難しいものばかりであるが、一つ一つ積み上げ無いと強くはなれない...
odashiさんが書かれたpythonで継承を実装する際のガイドライン
勉強になったところだけ抜粋
# from https://predicatejp.notion.site/Guidelines-for-inheritances-e9837f55d3774bb4b2f22776ba2d7386
class Vehicle(metaclass=ABCMeta):
@abstractmethod
def run(self) -> None:
...
継承を用いると、以下のようにis-aの関係が生じてしまうため
# from https://predicatejp.notion.site/Guidelines-for-inheritances-e9837f55d3774bb4b2f22776ba2d7386
class Vehicle(ABC):
...
# Vehicle accidentally has the "is-a" relationship with ABC.
assert issubclass(Vehicle, ABC)
以下のようなimplementation patternによる実装は不必要なis-aの関係を生じさせてしまう
# from https://predicatejp.notion.site/Guidelines-for-inheritances-e9837f55d3774bb4b2f22776ba2d7386
class Engine:
def start(self): ...
def stop(self): ...
# Car is-implemented-in-terms-of Engine.
# The inheritance must be hidden in this case,
# but Python does not provide such functionality.
class Car(Engine):
def drive(self):
self.start()
...
self.stop()
my_car = Car()
assert isinstance(my_car, Car) # Returns True.
assert not isinstance(my_car, Engine) # Also returns True!
以下のように別クラスで実装したものを内部に保有する、composition パターンで回避したほうがよい
class Engine:
def start(self): ...
def stop(self): ...
# Defining Car with composition.
# Implementation can be replaced by composition in most cases.
class Car:
def __init__(self):
self._engine = Engine() # Car has-an Engine.
def drive(self):
self._engine.start()
...
self._engine.stop()
my_car = Car()
assert isinstance(my_car, Car) # Returns True.
assert not isinstance(my_car, Engine) # Returns False.
以下のように重さの単位を変える便利関数をbase classに実装したくなる時があるが、この便利関数があらゆる継承先で本当に必要になるかを感がなければならない。
# https://predicatejp.notion.site/Guidelines-for-inheritances-e9837f55d3774bb4b2f22776ba2d7386
class Vehicle(metaclass=ABCMeta):
@abstractmethod
def weight(self) -> float:
# This function returns the physical weight of this object in kg.
...
def weight_in_lb(self) -> float:
# This function returns the physical weight of this object in lb,
# but converting the unit seems to be not a responsibility of this class.
return 2.20462 * self.weight()
class Car(Vehicle):
def weight(self) -> float:
return 1000.0
>>> Car().weight_in_lb()
2204.62
時に必要で、時に不要なのであれば、便利関数を切り出した方が得策である。
# https://predicatejp.notion.site/Guidelines-for-inheritances-e9837f55d3774bb4b2f22776ba2d7386
"""vehicle.py"""
class Vehicle(metaclass=ABCMeta):
@abstractmethod
def weight(self) -> float:
# This function returns the physical weight of this object in kg.
...
"""car.py"""
class Car(Vehicle):
def weight(self) -> float:
return 1000.0
"""weight_utils.py"""
# Utility function to convert the unit of weights.
def kg_to_lb(kg: float) -> float:
return 2.20462 * kg
>>> kg_to_lb(Car().weight())
2204.62
以下のようにbase classに継承先のインスタンス化機能を持たせてしまうと、メンテナンスも複雑になるし循環参照を生じさせてしまう。
# https://predicatejp.notion.site/Guidelines-for-inheritances-e9837f55d3774bb4b2f22776ba2d7386
"""vehicle.py"""
from mylib.car import Car # Circular dependency!
from mylib.train import Train # Circular dependency!
from mylib.airplane import Airplane # Circular dependency!
...
# We need to change the behavior of Vehicle every time a new subclass is added.
class Vehicle:
def create_vehicle(name: str) -> Vehicle:
if name == "car":
return Car()
...
"""car.py"""
from mylib.vehicle import Vehicle # Circular dependency!
class Car(Vehicle):
...
以下のように独立させるべし。
# https://predicatejp.notion.site/Guidelines-for-inheritances-e9837f55d3774bb4b2f22776ba2d7386
"""vehicle_factory.py"""
# This module is a sink of the dependency chain.
# There is no dependency from modules below to this module.
from mylib.vehicle import Vehicle
from mylib.car import Car
from mylib.train import Train
from mylib.airplane import Airplane
...
def create_vehicle(name: str) -> Vehicle:
if name == "car":
return Car()
...
メンテが大変になるのは想像に難くない。
# https://predicatejp.notion.site/Guidelines-for-inheritances-e9837f55d3774bb4b2f22776ba2d7386
@dataclass
class BaseData:
one: int
two: float
@dataclass
class MyData(BaseData):
three: str
one: str # Overridden!
実務で継承をあまり活用したことがなかったが、勉強になった。
他社や外部のパートナーに作ってもらったAPIと連携をする際の注意点が整理されているブログ記事。
なるほどと思ったところだけ抜粋
仕様書と実態の実装が違うことで、使い方が間違っているのか、現状が正しいのかわからず確認作業に時間を溶かすといったことが容易に起きえます。
定期的に仕様書が最新であるかのコミュニケーションを取るのが良さそう。
以下の要点を押さえておけばOK。
- 通信方式
- リクエストフォーマット / レスポンスフォーマット
- リクエストヘッダー (Content-Typeやアクセストークン)
- 操作制約 (時間制約, ネットワーク制約, リクエスト制約)
- ステータスコード (HTTP固有, 独自定義)
- エンドポイント / URLパス
- 文字コード
- 認証方式 / 認可方式
漏れなく抑えたい
以下の要点を押さえておけばOK。
- フィールドは必須なのか任意なのか
- 必須でも空文字である場合、何らかの意味判定をするのか
- フィールドがない場合の扱いをどう解釈するのか
- 共通して要求されるフィールドがあるのか/ないのか
- 数字の表現に特殊な法則はないのか
- 文字数制限を設けている意図は何か、超えた場合どうなるのか
- 配列である場合、件数制限はあるのか
仕様書だけだと発生する疑問は全て潰す。思いついた例外ケースも全て潰す。
リソースのライフサイクルというのは、リソース自体の作成、更新、削除といった状態遷移のこと。
フィールドのライフサイクルというのは、status といったフィールドで表現されるもので 、何らかの申請を行う場合には、申請前, 申請完了, 審査中, 承認, 非承認といった状態遷移が考えられます。
大凡の仕様書にはリソースに対する操作方法は明記されますが、自分達の操作以外で変わりうるケースが明記されていない場合があります。
ステータスの意味解釈は明記されていると思いますが、それがいつ, 何を起因に, どれに変わるのか, そこに制約はあるのか を把握するよう意識しましょう。
この手のAPIを作ったことはないが、確かに大事そう。
クライアント側で整合性を確認するには参照APIがなければできません。
当たり前だけ忘れそう
ネットワークエラーなどで、重複した操作を実施することが多々ある
例えば、特定のお客様に何らかの特典を付与するという場合に付与リクエストは通ったものの、レスポンス時にネットワークエラーが発生し、エラーだとみなして付与リクエストをリトライしたとします、この時エラーになったからといって安易にリトライしてしまうと、仮にAPIに冪等性がない場合、重複して特典を付与してしまうことになります。
以下の問いに答えを出しておくと良いかもしれません。
- 障害発生時にどの様に振る舞うのか
- システムが復旧したらいつから正常処理を再開するのか
- 何を基準に処理再開を判断して良いのか
- 再開前に整合性を確認する必要があるのか
- 双方のシステムで処理を経た結果、最終的なあるべき状態は何か
有事にアタフタしないように
問題発生時に誰にどの手段で連絡を入れるのか、時間帯的にいつ頃に連絡するのがよく、社内外で誰が表に立ってやりとりするのかを双方認識を揃えておきます。
有事の際のコミュニケーションパスを確保するのも大事だけど、定期的なコミュニケーションの場を用意するのも良さそう。
ブログはパートナーにAPIを作ってもらう想定で書かれているが、作り手側も同様なことを意識する必要がありそう。
作る場合はこれん加えて負荷とかも気にしないとなのかな?
理想の声を目指して 〜七声ニーナの音声変換技術からライブ配信応用へ〜
DeNAが2022年3月まで公開していた音声変換サービス七声ニーナに用いられている技術の概要とリアルタイム音声変換の課題が紹介されている。
ボイスチェンジャーとの違い
ボイスチェンジャーは利用者が調整する必要があるが、低遅延
音声変換は利用者がは特に意識する必要なく使えるが高遅延
音声変換の処理フロー
各モジュールで考えられるパターン
七声ニーナの音声変換アーキテクチャ
元サイトではイントネーションによる生成音声の違いを聞くことが可能
リアルタイム音声変換の作り方
リアルタイム音声変換は50 ~ 70 ms程度の低遅延さが求められる
しかし、実際にはブロックサイズを小さくしながら高性能な音声変換を実現するのはかなり難しい。
コメント
リアルタイム音声変換技術の難しさがなんとなくわかった。
ブロック長を短くしても高精度な変換ってのは何を仮定すればいいのだろうか...
出典
理想の声を目指して 〜七声ニーナの音声変換技術からライブ配信応用へ〜