ハイエンド PC が無くても Paperspace で手軽に MMD 動画を作成する
概要
MikuMikuDance (MMD) は樋口優さんが開発されたフリーソフトで、 3D モデルに動きを付けた動画を簡単に作成することができます。
MMD でクオリティの高い 3D モデルを扱ったり高解像度の動画を出力したりする場合には GPU を積んだ高性能な PC が必要になり、普通に購入すると少なくとも十数万円の出費になります。
そこで本記事では、 ハイエンド PC を所有していない人でも安価で手軽に MMD を始められる ように、 クラウドサービス (Paperspace) の GPU マシンを活用する方法を解説します。
クラウド (クラウドコンピューティング) とは
現在クラウドと呼ばれるサービスは非常に多岐に渡り、はっきりとした定義が存在するわけではありませんが、大まかに説明すると以下のような特徴が挙げられます:
- インターネットの向こうにあるコンピューターをレンタルして使用できる
- 高額な初期費用や面倒な初期設定が不要で、すぐに利用開始できる
- 料金は使用した分だけ (1 秒あたりの従量課金制などで)かかる
特に今回扱うのは、 GPU を積んだ Windows 仮装マシンをクラウド上で起動して、リモート操作で自分の PC のように使用できる サービスです。 これによって動画出力など高負荷な処理が全てクラウド上で行われ、その結果の画面やファイルだけを受け取ることができます。 そのため、手元にあるのが低スペックなノート PC でも、また OS が Mac や Linux でも関係なく動作します。
クラウドサービスの選択肢
クラウド最大手のプロバイダとして以下の 3 社が有名です。
しかしこれらはいずれも、ソフトウェア開発者や研究者を主な対象として、非常に幅広い用途に合わせたサービスを多数提供しています。 そのため、知識の無い人がいきなり利用するにはあまりに複雑すぎ、サービスの選択や細かい設定などで途方に暮れる恐れがあります。
そのため本記事では、上記のどれでもなく Paperspace というクラウドサービスを紹介します。
Paperspace の使い方
概略
Paperspace には Core と Gradient という別々のサービスがあり、今回使用するのは Core の方です1。
Core プランでは以下のような手順で Virtual Machine (VM) を起動して作業します。 (VM とはその名の通り、クラウド上で動作する仮想的な PC のことです。)
VM の設定
Paperspace の Web コンソール で "Create a Cloud VM" ボタンをクリックすると、 VM の設定を選択するページが表示されます。
VM を設置する地域は日本に近いほどネットワークの通信遅延が少なく快適になります。現状は US West 一択です。
OS は MMD が動作するように Windows を選択すれば (7 でも 10 でも) 問題ありません。
VM のタイプは、動画の出力スピードとクラウドの利用料金(後述)に直結する設定です。 パフォーマンスは使用するエフェクトや出力画質などに左右されるため、一概にどの VM タイプが良いとは言えません。 参考までに、私が最近作成した MMD 動画 (フル HD 60fps, UtVideo YUV420 BT.709, 再生時間 5 分 33 秒) を出力するのにかかる時間は
- Advanced タイプ : 約 11 分 40 秒
- Pro タイプ : 約 10 分
- P4000 タイプ : 約 4 分 10 秒
ストレージ容量もクラウドの利用料金に影響する設定です。 MMD が出力する avi 動画は基本的にギガバイト級のサイズになるため、 容量が足りなくならないように設定しましょう2。 目安として、前述の動画のファイルサイズが約 12 GB になります。
VM へのリモート接続
Paperspace の VM にリモート接続する際には、特別なソフトのインストールや設定は必要ありません。
VM を作成して数分待ってから VM の一覧ページ を確認すると、
On/Ready
という状態が表示された項目があるはずです。
これをクリックすると Web ブラウザ上にリモートデスクトップが表示されて VM を操作できるようになります。
MMD のインストール
通常の PC の場合と全く同じように、下記のソフトを VM にインストールします。
- MikuMikuDance (64bitOS Ver)
- Microsoft Visual C++ 2008 再頒布可能パッケージ (x64)
- Microsoft Visual C++ 2010 再頒布可能パッケージ (x64)
- DirectX エンド ユーザー ランタイム
- (必要に応じて) Ut Video Codec
- (必要に応じて) MikuMikuEffect
VM の 起動/停止/削除
VM の一覧ページ で歯車のアイコンをクリックすると、 その VM の管理ページが開きます。 ここで start / shutdown / deactivate ボタンを押すことで VM の 起動/停止/削除が可能です。
shutdown が一時的に電源を落とすようなイメージであるのに対して、 deactivate は VM のストレージに保存されているデータを全て削除します。
利用料金を節約するため、使い終わった VM はこまめに 停止/削除 しましょう。
Paperspace の料金体系
2 種類の料金がかかります。
- Virtual Machine (VM) 起動時間に対する従量課金
- ストレージ容量に対する月額課金
前者は VM が起動している間だけ課金されますが、 後者は VM が停止していてもデータを保持し続けるために料金がかかるので注意してください。 完全に課金を停止させるためには VM を削除する必要があります。
各 VM タイプやストレージ容量に対する具体的な料金は、公式のヘルプページから確認可能です。
まとめ
なお、下の招待リンクから Paperspace に登録すると $10 分のクレジットが貰えるので、もしよければご利用ください。
にじさんじマイクラ鯖を企業として管理するのは本当に規約違反か?
概要
VTuber グループの「にじさんじ」には、ライバー専用の大規模な Minecraft サーバー(通称にじ鯖)があります。にじ鯖は会社側で管理しているわけではなく、ライバーのドーラさんが自費で管理し善意で無償提供しているものです。
これに関して「Minecraft の商業利用規約に違反するため会社側でにじ鯖を管理できない」という噂を見かけたため、真偽を確かめるため実際に規約を確認しました。結論として、この噂は誤りである可能性が極めて高いです。
注意事項
- 本記事は筆者の個人的な解釈を述べたものです。 Mojang 公式の見解と一致する保証はありません。
- 筆者は「にじさんじ」や Mojang と一切関係がありません。
- 本記事の内容について「にじさんじ」の運営やライバーなどへ問い合わせるのはご遠慮ください。
背景
にじ鯖は最近、 10 月 2 日から約 3 週間のアップデートを挟んで新バージョンにアップデートされました。 その際の諸々の経緯について、管理者のドーラさんご本人が配信上で説明されています。
本記事で特に焦点を当てるのは、配信の一部で明言された下記の点です:
- にじ鯖の管理、費用の負担などは全てドーラさんが個人的に行っている
- 会社側で管理するのは、理由があって不可能
Minecraft に限らずサーバーの運用は専門的な知識を要します。非エンジニアでありながらライバー業と並行してにじ鯖の管理をするのは相当な負担となるはずです。 また、サーバーのレンタル代金も嵩みます1。 そのため、可能であれば会社側でエンジニアがにじ鯖を管理するのが理想的ですが、何らかの事情があってできないようです。
配信中のコメントを見ていると、「Minecraft の利用規約に違反する」のがその理由だと述べている方々がいらっしゃいました。 しかしこれは恐らく誤りだと私は考えており、その理由を以降で説明します。
本題
Minecraft の規約はこのページで確認できます。 特に、 Minecraft を利用した収益化に関しては、 商業目的使用ガイドラインとブランドおよび資産の使用に関するガイドラインに記載されています。
その中でも「規約に違反するため会社側で Minecraft サーバーを管理できない」という噂の元になっているのは、商業目的使用ガイドラインにある下記の一節だと思われます:
However YOU MAY:
- Pay for advertisements of your business to be served on websites or servers related to Minecraft, so long as you are not hiring the server operator to design or host a Minecraft mod/map/server that builds an in-world representation of your brand, products, or services.
公式の日本語訳
ただし、お客様は、以下を行うことができます。
この日本語訳は非常に不自然で読み辛く、主語や係り受けも曖昧になっています。 そのため、一見すると「広告収益を得る際にはサーバー管理者を雇用してはいけない」かのような印象を受けるかもしれません。 しかし原文を注意深く読めば、この条項が禁止しているのは
- 広告主が、自社製品を Minecraft のゲーム内に再現建築させる目的で、サーバー管理者に報酬を支払うこと
だとわかります。一方、いちから株式会社がにじ鯖のサーバー管理者を雇うのは
- 配信者の所属する企業が、配信に使用するサーバーを管理させる目的で、サーバー管理者に報酬を支払うこと
に当たるので、報酬を支払う主体も目的も全く異なり、上記の条項とは関係ありません。
補足
前述の条項に関する説明を読んで、恐らく次のような疑問を持たれるのではないでしょうか:
- この条項は何のためにあるのか?
- 「自社製品を Minecraft のゲーム内に再現建築」とは具体的に何を指すのか?なぜ禁止されているのか?
その答えは、開発者が投稿したブログ記事の中にあります。 前述の条項は 2016 年に新しく追加されたものですが、その背景や大まかな内容が説明されています。
We want to empower our community to make money from their creativity, but we’re not happy when the selling of an unrelated product becomes the purpose of a Minecraft mod or server.
…
If you are an ad agency, corporation, non-profit, or politician, you can’t do these kinds of things or hire someone to do them:
- Build a Minecraft mod or server that promotes unrelated products in playable form; e.g., if you are a restaurant chain, you can’t market your restaurant by releasing a mod that includes your restaurant built out of Minecraft blocks.
- Build a Minecraft map or mod that markets a movie or TV show; e.g., if you’re a movie studio, you can’t make a map that uses Minecraft blocks to build out the fictional world of the movie or its characters, and you can’t make an official movie trailer out of gameplay footage from that map or mod.
- It’s worth mentioning that fans of a particular restaurant, movie, or some other thing are still free to build things in Minecraft that represent or celebrate it so long as the goal or focus is not to promote or sell that stuff. The new rules only apply to companies or organisations that are using Minecraft to sell their products or promote their causes.
日本語訳
Minecraft ユーザーが創造性を発揮して収益を得ることは奨励していますが、サーバーや Mod の主目的が無関係な商品の販促になってしまうのは好ましくありません。
(中略)
あなたが 広告代理店 / 企業 / 非営利組織 / 政治家 に該当する場合、下記に挙げる行為をしてはいけません。また、第三者を雇って行わせることも禁止します。
- Minecraft と無関係な商品を宣伝するような、プレイアブルなサーバーや Mod を作成すること。例えば、レストランチェーンが Minecraft のブロックで自社レストランを建築してマーケティングに利用するのは禁止されています。
- 映画やテレビ番組を宣伝するようなマップや Mod を作成すること。例えば、映画スタジオが Minecraft のブロックで映画の世界観やキャラクターを再現したり、それを用いて映画のトレイラーを作成したりするのは禁止されています。
注意点として、ファンが特定の商品などを Minecraft 上で再現建築するのは、宣伝が目的でない限り自由に行って構いません。今回新しく追加される条項は、企業が Minecraft を利用して自社のプロモーションをする場合にのみ適用されます。
この記事には書かれていませんが規約改定前に実際に起きた例として、携帯電話事業者の Verizon が Minecraft 内で実際に動作するスマートフォンを建築したことがあります。 これも現在の規約ならば違反行為にあたるものです。
結論
- 「いちから」がにじ鯖の管理者を雇ったとしても、 Minecraft の規約違反にはならないと考えられます。
- 規約の不自然な日本語訳が原因で拡大解釈されている恐れがあります。
- にじ鯖の管理者を雇えない理由は Minecraft の規約以外にも有り得るので、本記事を見て「にじさんじ」に問い合わせないでください。
-
参考までに、 Conoha の VPS で Minecraft サーバーを立てる場合、プレイヤー数 11 人以上のプランでは月額 6,670 円かかります。アップデート後のにじ鯖のプレイヤー数は最大 35 人です。↩
Doneru についてのまとめ
概要
Doneru とは「Twitch・YouTubeのライブ配信者を支援する拡張プラットフォーム」で、 YouTube のスーパーチャットと類似した投げ銭機能などがあります。 この Doneru を、 VTuber 事務所「にじさんじ」に所属するライバーが使用したことがきっかけとなり、 Doneru が YouTube の規約に反するのではないかとネット上の一部で騒動が起きています。 本記事の目的は、この騒動に関する事実、デマ、私なりの意見をまとめて論点を整理することです。
最終的な私の意見の要点は:
- Doneru は規約、倫理的には何も問題無いと考えられる
- Doneru を使うことで YouTube から BAN されるとは考え難い
- 現時点での Doneru は規約以外の面で問題が多く、信頼して実用できるレベルではない
起きた出来事
Doneru は 2019 年 7 月末にオープンβ版としてリリースされた新興のサービスです。 その後少し経って 9 月 12 日、「にじさんじ」所属の御伽原江良さんと椎名唯華さんが Doneru を導入した配信を行いました。 (このうち、御伽原江良さんの配信アーカイブは現在でも閲覧可能です。) 両名は「にじさんじ」の中でも特にフォロワーが多いためこの配信が注目を浴び、 Doneru や「にじさんじ」運営に対して様々なお気持ち表明をなさる方々が現れています。 その中でもよく見かける批判は概ね次の通りです:
これを受けて、 Doneru の公式 Twitter アカウントから回答がなされました。
当該サービスに関する判断をYouTube側に確認しております。
— 【公式】Doneru(どねる) (@Doneru_JP) September 13, 2019
この度は利用者の皆様にご迷惑をおかけし大変申し訳御座いません。
正式な返答を受領後、公式アナウンス致します。
しかし、サービスの正当性の確認がリリース前にされていなかったことが再び批難を浴び、更なる騒動に発展しています。
Doneru 導入の背景
そもそも YouTube には、視聴者から配信者へ投げ銭できるスーパーチャット機能が既にありますが、あえて外部の投げ銭サービスを利用するメリットは何でしょうか? 収益化の面で主に以下の 2 点が挙げられます。
メリット 1 は既に有名な話なので、あえてここで詳細に取り上げることはしません。 一方でメリット 2 は比較的話題に上っていませんが、非常に重要です。
YouTube の収益化に対する締め付けは年々厳しさを増しており、規約違反と判定されて収益化を解除される例が頻発しています。VTuber 界での事例の一部を挙げると:
- キズナアイさんのチャンネルが無警告で BAN されたがすぐ復活した1
- 鈴鹿詩子さんのゲーム配信が「ヌードや性的なコンテンツ」と判定されてチャンネルが無警告で BAN された2
- ベイレーンさんが「繰り返しの多いコンテンツ」と判定されて収益化解除された3
- ふぇありすさんが「性的満足を意図したコンテンツ」と判定されて収益化解除されている4
これらの例は、中身をよく見てみればどんな人間でもおかしな判定だとわかるものです。 しかし YouTube では、コンテンツの最初の審査を bot が担当しているため誤審査がよく起こります。 異議申し立てをすれば人間による再審査が行われますが、申し立てができるのは一ヶ月後で、審査が覆る確率もそれほど高くないようです。 つまり、真っ当なチャンネルがある日突然に規約違反と判定されて、その後少なくとも一ヶ月間は収入が大幅減、最悪引退に追い込まれるリスクがあります。
YouTube 外部のサービスに収益源を分散しておけば、こうしたリスクをある程度軽減することが可能です。
ネット上の的外れな批判
今回の件について SNS などを見ていると、一部真っ当な批判もありますが、大半は全く見当違いな言いがかりやデマで溢れています。 そこで、明らかにおかしいと根拠を持って否定できる意見を以下で取り上げ、私なりの回答を述べます。
YouTube で配信しているのだからスーパーチャットを使え
配信者にそのような義務や義理はありません。理由は主に以下の 3 点です。
- スーパーチャットは独立したオプション機能であり、使用するか否かはユーザー(配信者)の自由である。
- YouTube の利用規約や収益化ポリシーには、外部サービスによる収益化を禁止するような条項は無い。
- スーパーチャットの使用をもし強制すれば、それは巨大プラットフォームの力を濫用した囲い込みであり、市場競争を妨げる。
スーパーチャットを使わないのは、場所代を払わないタダ乗り行為である
「スーパーチャットを使わない」ことと「YouTube の利益に貢献しない」ことは全く異なります。 YouTube の主な収益源は、広告費と、ユーザーが生み出す行動履歴などの膨大な個人データです。 これはスーパーチャットの有無とは関係なく YouTube 上で大量の視聴者を集めれば必ず発生するものなので、そもそもタダ乗りは不可能です。
Doneru は中間マージンを取っているが Streamlabs はそうではないので同じ座組ではない
今回の件で YouTube 側にとって問題となり得るのは、外部サービスによってスーパーチャットの収益機会が失われるというただ一点だけであり、 その点において Doneru も Streamlabs も共通しています。中間マージンの有無やビジネスモデルの違いは問題に関係ありません。
Doneru は YouTube の承認済みクラウドファンディングサイトに記載されていないから使用できない
このページにあるのは、動画の再生終了直前に表示されるエンドカードにリンクとして設定できるサイトの一覧です。 ここに記載されていないサイトでも、動画内や概要欄でページを宣伝するのは問題ありません。 (もし禁止されていたら、いわゆる企業案件のアフィリエイトリンクなどは全て規約違反ということになります。)
Doneru を使うと YouTube アカウントが BAN される
上述したように Doneru の使用は YouTube の規約に反していないので、特に BAN される理由が考えられません。
Doneru ではなく YouTube 公式に承認されている Streamlabs を使え
Streamlabs の donation page は日本語表示に対応していません。
「にじさんじ」運営は所属ライバーのことを考えず拝金主義に走っている
ライバーのことを考えるのであれば、ライバーの収益を増やす/安定化させるのは最重要事項です。
Doneru の問題点
Doneru が規約や倫理的には問題ない旨をここまで説明しましたが、それ以外の部分で後述する大きな欠陥を抱えています。 ただし、対処策が明らかな問題ばかりなので、今後の修正により完成されたサービスになる可能性はあります。
投げ銭時のメッセージや音声読み上げが荒らしに悪用される
通常のチャットログに埋もれて流れやすいスーパーチャットと違って、 Doneru の投げ銭に添えられるメッセージは専用の枠に表示されかなり目立ちます。 メッセージの音声読み上げがオンにされていれば尚更です。
これを悪用してセンシティブな内容を配信に投げつけることが可能で、実際に「にじさんじ」のライバーが Doneru を使った際にこうした荒らし行為が見られました。
サイトの作りが全体的に雑で、セキュリティを考慮していない可能性が高い
存在しない URL を指定すると、通常はそのことを示す 404 エラーページが表示されますが、 Doneru のサイトではエラーページの設定が正しくされておらず、本来ユーザーに対して表示すべきでないシステムログが露出しています。 例えば https://doneru.jp/example にアクセスすると、以下の内容のページが表示されます。
Not Found 404 NotFoundError: Not Found at /var/www/doneru-dev/app.js:95:8 at Layer.handle [as handle_request] (/var/www/doneru-dev/node_modules/express/lib/router/layer.js:95:5) at trim_prefix (/var/www/doneru-dev/node_modules/express/lib/router/index.js:317:13) at /var/www/doneru-dev/node_modules/express/lib/router/index.js:284:7 ⋮
三行目以降の部分はスタックトレースと呼ばれ、開発者がエラーの原因を特定するために用いるものです。 スタックトレースはユーザーが見ても理解できるものではなく、またシステム内部の情報を悪意ある攻撃者に与える恐れがあります。 そのため、製品段階のサイトではこのようなシステムログをユーザーに一切見せないのが常識とされています5。
この他にも、フォームの入力内容を検証せずに送信するなど多数の問題が挙げられます。
個人的な感想
YouTube 一強の状態でクリエイター側が不利益を被っている中、それを解決できそうなサービスが現れるのは嬉しい限りです。 その際に何か問題点が想定できるのであれば、ただの推測で終わらせずに実験で検証すべきだと考えています。
「わざわざ金を払ってまで配信で嫌がらせをする生物が多くいる」というのは検証が必要な仮説だと私は思っていたのですが、 SNS 上の皆様には至極当然で確実に予測できる事実だったようです。
BERT 日本語モデルの実験
概要
BERT (arxiv, GitHub) を理解するための第一歩として、訓練済み日本語モデルを fine-tune して文章のトピック分類の実験をしました。
この記事に含まれない内容:
- BERT の説明
この記事に含まれる内容:
- 訓練済み BERT 日本語モデルのまとめ
- 環境構築や実験にあたって私が遭遇した問題とその対処
- BERT が分類に失敗したサンプルの定性的な分析
訓練済みの BERT 日本語モデル
現時点で私の知る限り、日本語に適用できる訓練済み BERT モデルは以下の4つがあります。特に今回の実験では、
という理由で BERT with SentencePiece for Japanese text を選択しました。
BERT-Base, Multilingual
BERT の GitHub レポジトリで公式に提供されている多言語モデル。使っているトークナイザーが日本語に適しておらず、過剰に小さな(ほぼ 1 文字単位の)トークンに分割されてしまうことが報告されています*1。
BERT with SentencePiece for Japanese text
菊田遥平さんが提供しているモデル。前述のトークンの問題に対処するため SentencePiece を使用しています。
hottoSNS-BERT
ホットリンクの R&D 部が提供しているモデル。ここに挙げたモデルの中で唯一 Twitter データをコーパスに使っています。
- GitHub
- ブログ記事
- トークナイザー : SentencePiece
- コーパス : 日本語ツイートのコーパス(ホットリンク社内製、非公開)
BERT日本語Pretrainedモデル
京大の黒橋・河原研究室が提供しているモデル。当然ながら、トークナイザーには自製の JUMAN を採用しています。
TensorFlow + GPU 環境構築
私の研究室は GTX 1080 Ti を 2 枚積んだ GPU サーバーを所有しているのですが、極端に計算パワーを必要とする研究を私は今までしていなかったため、一度も使ったことがありません。今回は金をかけずにとりあえず BERT を試したかったこともあり、良い機会なのでこの GPU サーバーを使うことにしました。
しかし案の定と言うべきか、最初に環境を構築する段階で多数のエラーに見舞われかなり時間を費やしたので、その記録を残しておきます。
tensorflow-gpu インストール時のエラー対処
TensorFlow で GPU 計算をするためには tensorflow-gpu
パッケージが必要です((https://www.tensorflow.org/install/gpu)。これをインストールした後に Python で import tensorflow
を試みた時に以下のエラーが発生しました。
ImportError: libcublas.so.10.0: cannot open shared object file: No such file or directory
issue を見る限り、 TensorFlow と CUDA のバージョンが合致しない時にこのエラーが発生するようです。そこで、サーバーにインストールされていた CUDA のバージョンを確認すると
$ cat /usr/local/cuda/version.txt CUDA Version 9.2.148
TensorFlow 最新安定版の 1.13.1 では CUDA 10 しかサポートしていない*4ので、どちらかのバージョンを変更する必要があります。管理者権限無しで手っ取り早く対処するために TensorFlow の 1.12.0 をインストールしようとしたところで再びエラーに遭遇しました。
ERROR: Could not find a version that satisfies the requirement tensorflow-gpu==1.12.0 (from versions: 1.13.0rc1, 1.13.0rc2, 1.13.1, 2.0.0a0)
もちろんバージョン 1.12.0 は存在していますが、 Python 3.7 と互換性が無い*5ためにインストールできなかったようです。これには Python 3.6 の仮想環境を作成してから TensorFlow をインストールすることで対処しました。
$ conda create -n bert python=3.6 $ conda activate bert $ conda install -y tensorflow-gpu==1.12.0
最後に、 tensorflow-gpu
が正常にインストールされて GPU が使用可能であることを確認します。
$ python -c "import tensorflow as tf; tf.test.is_gpu_available()" 2019-05-28 15:18:53.502786: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: SSE4.1 SSE4.2 AVX AVX2 AVX512F FMA 2019-05-28 15:18:54.302067: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1432] Found device 0 with properties: name: GeForce GTX 1080 Ti major: 6 minor: 1 memoryClockRate(GHz): 1.582 pciBusID: 0000:17:00.0 totalMemory: 10.92GiB freeMemory: 10.77GiB 2019-05-28 15:18:54.377021: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1432] Found device 1 with properties: name: GeForce GTX 1080 Ti major: 6 minor: 1 memoryClockRate(GHz): 1.582 pciBusID: 0000:65:00.0 totalMemory: 10.91GiB freeMemory: 10.77GiB
GPU の状態監視
nvidia-smi コマンドを使うと、 GPU の使用率などを確認することができます。
特に --format=csv
オプションを指定すると結果が CSV 形式で出力されるので、スクリプトで読み込んで自動的に通知すると便利です。
私はこのレポジトリ に少し手を加えて Python 3 用に修正し、 GPU の状態を Slack の web hook に通知するようにしています。
メモリ解放
Jupyter Notebook 上で TensorFlow による GPU 計算が終わった後に状態を見ると、 GPU 使用率が 0% の一方で大量のメモリが占有されたままになっていました。ドキュメントを読む限り、これは特に異常なことではありません。メモリのフラグメンテーションを避けるために、 TensorFlow は プロセスが終了するまで GPU メモリを一切解放しない 仕様になっているようです。
Jupyter で GPU 計算が完了した後は忘れずにカーネルをシャットダウンしましょう。
実験
方法
finetune-to-livedoor-corpus.ipynb をほぼそのまま使用しました。
ただし、実験時に GPU メモリの空き容量が 7GB 程度しか無かったため、 run_classifier.py
の引数 train_batch_size
を 4 から 2 に下げて実行しました*6。
データ
livedoor ニュースコーパス (NHN Japan株式会社がクリエイティブ・コモンズライセンス「表示 – 改変禁止」のもと提供)
livedoor ニュースの 9 つのカテゴリごとに収集した記事のテキストデータです。全部で 7376 個の記事があり、各カテゴリごとの内訳は表のようになっています。
カテゴリ | 記事数 | 備考 |
---|---|---|
トピックニュース | 771 | 芸能ニュースなど |
Sports Watch | 901 | |
IT ライフハック | 871 | |
家電チャンネル | 865 | |
MOVIE ENTER | 871 | |
独女通信 | 871 | |
エスマックス | 871 | スマホ、モバイル、ガジェットなど |
livedoor HOMME | 512 | 男性向け、キャリア、デジタルなど |
Peachy | 843 | 女性向け、恋愛、ファッションなど |
結果
f1-score などの数値は、 finetune-to-livedoor-corpus.ipynb の先頭に記載されている結果と大差無いため割愛します。
BERT が「不正解」の予測をした全 41 件のサンプル (csv ファイル) を見てみると、もう少しおもしろいことがわかります。例えば以下の記事を読んで、 livedoor ニュースの 9 つのカテゴリのどれに該当すると思いますか?
手のひらに載る本格派 LUMIX G2のGはグレートだ今やマイクロ一眼はデジタルカメラの人気商品となり、各社とも続々とこの市場に参入を開始している。マイクロ一眼は、レトロなスタイルに加え、ミラーレスによる小型化でバックに入れて持ち歩けるデジタル一眼として年齢を問わずに使えるところが受けている。とはいえ、今人気のマイクロ一眼には、コンパクトデジカメやレンズ一体型デジカメに比べて高画質というメリットはあるが、デメリットもある。パナソニック LUMIX DMC-G2は、マイクロ一眼のデメリットを解消したカメラなのだ。 (後略)
実際のカテゴリは「livedoor HOMME」で、一方 BERT が予測したカテゴリは「IT ライフハック」でした。また、その次に予測確率 で「家電チャンネル」が続きます。もうお気づきかと思いますが、これら 3 つのカテゴリは排反なものではなく重複があってもおかしくありません。上述のサンプルであれば「livedoor HOMME」よりむしろ「家電チャンネル」か「IT ライフハック」の方が尤もらしいカテゴリに感じます。
classification の問題は、カテゴリが排反か否かによって"multi-class" と "multi-label" に分けられます。しかし今回の実験設定には以下のようなミスマッチがあり、それが原因で「正解より予測の方が妥当」という状況が発生しています。
- カテゴリは実際には multi-label であるべき
- 正解データは multi-class
- 今回用いた fine-tuning のコードは multi-class 用(softmax で確率を出力)
他の不正解サンプルについても「正解より予測の方が妥当」が大半を占めており、本当に予測ミスだと思われるものは 6 件 (csv ファイル)しかありません。 例として、以下の記事は実際には「livedoor HOMME」ですが、BERT はこれを「Peachy」と予測しました。
イケメン社長、野菜を売る——、若林輝男さんに聞く“農業ビジネスのカタチ”生産者と消費者が直接結びつく新たな市場として、農林水産省の補助事業として昨年9月に始まり、全国9都市で開催されるようになったマルシェジャポン。晴れ渡った3月の休日、大勢のお客さんで賑わう六本木の会場では、なんと“生姜(しょうが)のみ”を売る背が高くルックスのよい一人の男性を発見しました。土日は都内のマルシェジャポン会場で、平日は三輪自転車を走らせ都心の駅前を中心に、高知産の新鮮な生姜や青森産の大蒜(にんにく)を販売しているリーベル・アソシエイツ合同会社の代表=若林輝男さんです。 (後略)
結論
Google Colab 使った方が手っ取り早く実験できると思います。
*1:https://techlife.cookpad.com/entry/2018/12/04/093000
*2:https://github.com/google-research/bert/blob/master/multilingual.md#tokenization
*3:https://github.com/google-research/bert/blob/master/multilingual.md#data-source-and-sampling
*4:https://www.tensorflow.org/install/source#linux
*5:https://github.com/tensorflow/tensorflow/issues/23478
*6:https://github.com/google-research/bert#out-of-memory-issues
にじさんじの Twitter フォロワー分析
概要
にじさんじに所属する全ライバーの Twitter フォロワー情報を取得し、
- にじさんじ全体の(重複を除く)フォロワー数
- 1人のファンがフォローするライバー数の分布
- フォロワーの重複度合いを基に(t-SNE で)図示したライバーの位置関係
を分析しました。
背景
にじさんじは、執筆時点で合計 73 人のライバーを抱える最大規模の VTuber グループです。他のグループや事務所と比較して特徴的な点は:
- 編集された動画よりも長時間のライブ配信がメイン
- グループ内でのコラボが活発
- 非常に速いペースで所属ライバーが増え続けている
特に最近はにじさんじ全体で YouTube チャンネルの登録者が大きく伸びているため、ファンの一人として嬉しく感じています。 一方で、「ライバーの増加により延べ登録者数が増えただけで、新規層の獲得はできていない」のではないかと疑問を抱いていました。 というのも、「頻繁にコラボ配信をするライバー同士は、視聴者層が重複しやすい」と予想できるため、「一人で多数のにじさんじライバーをフォローしている人が多い」だろうと考えられるからです。
そこで、上記のカギカッコで囲まれた推測の真偽を検証するために、にじさんじライバーの Twitter フォロワーを分析しました。
分析結果
にじさんじ全体のフォロワー数
にじさんじに所属するライバーの全フォロワー ID を取得しました。これを基ににじさんじ全体のフォロワー数を合計した結果:
- ID の重複もカウントすると約 430 万人
- ID の重複を除外すると約 47 万人
となりました。
1人のファンがフォローするライバー数の分布
各フォロワー ID ごとに出現回数をカウントすることで、「にじさんじのフォロワーは 1 人あたり何人のライバーをフォローしているか」を計測しました。 その結果をヒストグラムにしたのが下図です。横軸はフォロー中のライバー数、縦軸は該当するフォロワー数(対数スケール)を表しています。
また、同じ結果を以下の表にも示しました。
フォロー中のライバー数 | 該当フォロワー数 |
---|---|
1 | 182385 |
2 | 60070 |
3 | 36276 |
4 | 23799 |
5 | 17270 |
... | ... |
69 | 1164 |
70 | 1082 |
71 | 1854 |
72 | 1603 |
73 | 5991 |
おもしろい特徴として、 フォロー 72 人から 73 人(全ライバー数)のところで該当数が大きく跳ねているのが見て取れます。 「数人取りこぼすくらいならいっそ全部フォローしたい」というコレクター精神が働いた結果でしょうか。
ライバーの位置関係の図示
2 つの Twitter アカウントのフォロワーを比較して、「フォロワーの重複の度合い」を数値のスコア(Dice 係数など)で表すことができます。 そのスコアを t-SNE と呼ばれるアルゴリズムに入力して、にじさんじライバーの位置関係を 2 次元のグラフ上で視覚化しました。このグラフでは
- フォロワーの重複が多いライバーはグラフ上で近く
- フォロワーの重複が少ないライバーはグラフ上で遠く
という関係を満たすように、各ライバーを表す点を自動的に配置しています。
上図を見ると、デビュー時期が近いライバーが近くに固まって配置されています。 よりわかりやすくするため、にじさんじ統合前のグループと、統合後に同時期にデビューしたライバーたちを囲んで下図に示しました。
更に、大きなクラスタの中に着目すると、 JK 組や舞元力一など頻繁にコラボ配信をするライバー同士が特に密集していることもわかります。
この結果から、「デビュー時期が近かったり頻繁にコラボ配信したりするライバー同士は、フォロワーが重複しやすい」という至極当然な推測が裏付けられました。
技術的な詳細
今回の分析には Python と Jupyter Notebook を使用しました。
Twitter API
にじさんじライバーのリストの取得
にじさんじ公式の Twitter アカウントが全ライバーのリストを作成しているので、今回はそこからライバーの情報を入手しました。
Twitter API の lists/members にリクエストすると、リストに含まれている全ユーザーの情報( ID 、スクリーンネーム、アイコン画像など)を取得できます。このとき、リストの owner_screen_name
と slug
の指定が必要です。リストの URL は
https://twitter.com/{owner_screen_name}/lists/{slug}
の形式になっているので、手動で確認することもできます。また、 owner_screen_name
がわかれば /lists/list から slug
を取得することも可能です。
フォロワーの取得
Twitter API の followers/ids に対して、ユーザー ID (e.g. 958737597336928256
)またはスクリーンネーム (e.g. @MitoTsukino
) を引数に入れてリクエストすると、全フォロワーのユーザー ID を取得できます。
1 回のリクエストでは最大 5000 件のフォロワーしか取得できず、また、リクエストは 15 分あたり 15 回までに限定されています。従って、記事執筆時点でのにじさんじフォロワー延べ 430 万人 を全て取得するためには 14 時間以上必要になります。
集合の類似度
任意のライバーのペアに対して、フォロワー ID の集合の類似度を Jaccard 係数、 Dice 係数、 Simpson 係数で計算しました。 notebook では 3 種類の類似度全てについて埋め込み結果を図示しましたが、この記事には Dice 係数を用いた結果だけを掲載しています。
t-SNE による 2 次元埋め込み
次元圧縮
ライバーに関する距離行列が前述の類似度によって与えられるので、次元圧縮で 2 次元ユークリッド空間に埋め込み可視化します。次元圧縮のアルゴリズムとして有名なものに
- Multi-Dimensional Scaling (MDS)
- Isomap
- Laplacian Eigenmap
- t-distributed Stochastic Neighbor Embedding (t-SNE)
などがありますが、今回は特に t-SNE を使用しました。
パラメーター調整
t-SNE には perplexity というパラメータが存在します。原論文によれば:
The performance of SNE is fairly robust to changes in the perplexity, and typical values are between 5 and 50.
とのことですが、実際には perplexity の値によってかなり結果は変動します。少なくとも、 sklearn のデフォルト値をそのまま使うと、収束前に停止したり解釈困難な結果を出力したりすることが多々あります。
再び原論文から引用すると:
The perplexity can be interpreted as a smooth measure of the effective number of neighbors.
これは、 個の要素に関する一様分布の perplexity が になる事実から類推できます。 "the effective number of neighbors" はデータの性質やサンプル数によって大きく変動するはずなので、それに合わせて perplexity も設定する必要があると考えられます。特に今回はサンプル数が 73 と少ないので、 perplexity を 5 に設定して実験しました。
perplexity の影響については、この解説記事も参考になります。パラメーターを変えた結果をインタラクティブに確認できるので、ぜひ一度試してみてください。
結論
分析結果を踏まえると、最初に述べた推測はいずれも妥当なものだと言えそうです:
- ライバーの増加により延べ登録者数は増えたが、新規層はあまり獲得できていない
- 一人で多数のにじさんじライバーをフォローしている人が多い
- 頻繁にコラボ配信をするライバー同士は、視聴者層が重複しやすい
1 人のファンが実際に追いきれるライバーの数には限度がある(長時間の配信が多いと尚更)ため、もしこのままの状況が今後も続くと、にじさんじ全体の収益はほぼ頭打ちになってしまうかもしれません。
万能な情報整理アプリ Notion のレビュー
Notion は、メモやタスク管理、その他情報を整理するための Wiki やデータベースとして使える万能なアプリです。Notion を一ヶ月ほど使用してみてわかった利点や欠点をまとめて記事にしました。
はてなブログにアニメ gif を貼ろうとして失敗したので、Notion 上で書いた原稿をそのまま公開します:
https://www.notion.so/Notion-f1b8c66221094286b3aea3e264203d63
PySnooper の中身を覗く
概要
こちらのブログ記事で、 PySnooper というデバッグ用の Python ライブラリが紹介されていました。
どんな仕組みで実装されているか気になり、またソースコードの行数も大したことがなかったので、実際に中身を覗いてわかったことをこの記事にまとめます。
PySnooper とは
関数定義にデコレーターをつけるだけで、実行中にソースコードの各行でローカル変数がどんな値を取っているか確認することができます。
例えば、以下のようなスクリプトを実行すると:
from pysnooper import snoop @snoop() def func(x): a = x + 1 b = a * 4 return a + 2 func(1)
標準出力に以下のようなトレース結果が表示されます:
Starting var:.. x = 1 23:06:03.976593 call 4 def func(x): 23:06:03.976593 line 5 a = x + 1 New var:....... a = 2 23:06:03.976593 line 6 b = a * 4 New var:....... b = 8 23:06:03.976593 line 7 return a + 2 23:06:03.977593 return 7 return a + 2 Return value:.. 4
ソースコード探訪
snoop
関数
PySnooper でユーザーが使用する API は基本的にデコレーター関数の snoope
だけです。
というわけで、まず snoope
が実装されているソースコードから見ていきましょう。
最初の数行は、トレース結果の出力先(stderr
など)を指定するためのもので、本筋とはあまり関係が無いので無視します。
その直後の decorate
関数の定義を見ると、ほとんどの処理を Tracer
クラスに委譲していることがわかります:
def decorate(function): target_code_object = function.__code__ tracer = Tracer(target_code_object=target_code_object, write=write, truncate=truncate, variables=variables, depth=depth, prefix=prefix, overwrite=overwrite) def inner(function_, *args, **kwargs): with tracer: return function(*args, **kwargs) return decorator.decorate(function, inner)
Tracer
クラス
__enter__
, __exit__
関数
上述の decorate
関数の中では、 Tracer
クラスのオブジェクトが with
構文で使われています。
つまり、デコレートする関数を呼び出す前後に Tracer
クラスの __enter__
, __exit__
関数がそれぞれ呼び出されるということです。
そこで、これらの関数の定義を確認すると、 __enter__
で sys.settrace
を使ってトレース関数を設定し、 __exit__
でそれを元に戻していることがわかります:
def __enter__(self): self.original_trace_function = sys.gettrace() sys.settrace(self.trace) def __exit__(self, exc_type, exc_value, exc_traceback): sys.settrace(self.original_trace_function)
sys.settrace
について
sys — System-specific parameters and functions — Python 3.7.3 documentation
sys.settrace(trace)
を実行すると、システムのトレース関数として trace
を設定します。その後以下のいずれかのイベントが発生したときに trace
が呼び出されます:
call
: 任意の関数を呼び出すときline
: ソースコードの任意の1行を実行するときreturn
: 任意の関数またはコードブロックが値を返すときexception
: 例外が発生するときopcode
: opcode を実行するとき
このとき、 trace
は3つの引数 frame, event, arg
と共に呼び出されます:
frame
: 呼び出し時のスタックフレームの状態を表すオブジェクト。このドキュメントに記載されているframe
型の属性をもつevent
: 上述のいずれかのイベントを表す文字列arg
: イベントがreturn
のときは関数の返り値、イベントがexception
のときはタプル(exception, value, traceback)
trace
関数
トレースすべき関数か判定
snoope(depth=k)
でデコレータされた関数の呼び出しと、そこから k
層分のスタックだけがトレースの対象になります。
すなわち、以下のいずれかの条件を満たす場合だけトレースが行われます:
- 現在実行中の関数
frame.f_code
とデコレータされた関数self.target_code_object
が一致する - 現在のスタック
frame
から呼び出し元f_back
をk
回以下辿ることで、デコレータされた関数に到達できる
変更された変数を表示
以下の部分で、前回トレース時の変数の状態 old_local_reprs
と現在の変数の状態 local_reprs
を取得します:
self.frame_to_old_local_reprs[frame] = old_local_reprs = \ self.frame_to_local_reprs[frame] self.frame_to_local_reprs[frame] = local_reprs = \ get_local_reprs(frame, variables=self.variables)
frame.f_locals.items()
によって、現在のスコープ中にあるローカル変数の名前と値を全て取得できます。
また、 self.variables
は snoop(variables=(x, y))
のようにオプション指定されたスコープ外の変数を表します。
old_local_reprs
と local_reprs
が得られたら、これらを比較して変更または新規に追加された変数を特定し、結果を出力します。
実行中のソースコードの行を表示
frame
からソースコードを取得する処理は get_source_from_frame
関数で定義されています。
キャッシュの参照や、 IPython の場合その他の例外処理を除いて、重要な部分だけに着目すると:
module_name = frame.f_globals.get('__name__') loader = frame.f_globals.get('__loader__') source = loader.get_source(module_name)
これが source = source.splitlines()
で行ごとのリストに変換して返されます。その後 trace
関数の中で以下のようにして実行中の行だけを抽出します:
line_no = frame.f_lineno
source_line = get_source_from_frame(frame)[line_no - 1]
トレース関数の引数から取得できる情報を実験的に確認
PySnooper の挙動を大幅に簡略化して再現し、実際にソースコードやローカル変数を取得できるか確認しましょう。 以下の Python スクリプトを保存して実行します:
import sys def func(x): a = x + 1 b = a * 4 return a + 2 def trace(frame, event, arg): if event == 'line': module = frame.f_globals.get('__name__') loader = frame.f_globals.get('__loader__') source = loader.get_source(module) lineno = frame.f_lineno line = source.splitlines()[lineno - 1] print(line) if event == 'return': print(frame.f_locals.items()) return trace sys.settrace(trace) func(1)
すると標準出力に以下の結果が print され、確かにソースコードやローカル変数を取得できることがわかります:
a = x + 1 b = a * 4 return a + 2 dict_items([('x', 1), ('a', 2), ('b', 8)]) dict_items([])