そぬばこ

備忘録とか、多分そんな感じ。

gokart + PyTorch Lightning でいい感じに深層学習モデルを動かす

この記事は Sansan Advent Calendar 2021 20日の記事です。

前日は、id:kur0cky さんの

kur0cky.hatenablog.com

でした。

私は過去陸上部だった時代があるのですが、個人的にはフォームも気にしたほうがいいと思います(感想)。

この記事は何か

私が所属している研究開発部には、パイプラインに則ってコードを書こうという文化が浸透してきました。 これらのパイプラインのパッケージは、弊社の研究員が弊社ブログにて既に様々書いているので、ぜひこちらを御覧ください。

buildersbox.corp-sansan.com

buildersbox.corp-sansan.com

さらに、PyTorch のラッパである PyTorch Lightning はいいぞとの布教をとある研究員の方から受け、良さそうだなとなりました。 PyTorch Lightning については、とある研究員の方がいつだかに書いた記事もぜひご覧ください。

buildersbox.corp-sansan.com

こういったパイプラインやラッパは処理の切り分けが明確になることで、コードの可読性を向上させたり等様々なメリットがあります。 せっかくなので私もこのびっぐうぇーぶに乗って、パイプラインである gokart と PyTorch Lightning を組み合わせてサクッとコードを書いてみようと思います。

準備

今回は gokart を使うので、雑に cookiecutter からテンプレートを持ってきて使います。 エムスリーさんが用意しているものがあるので、ありがたく使っていきます。

github.com

次に、 PyTorch を poetry で入れていきましょう。

ところで、PyTorch と poetry はボチボチ相性が悪いです。 PyTorch はアーキテクチャ等の環境に沿ったものを入れないといけませんが、例えば、特定の source から参照する方法で入れることを試みることができます。

github.com

一方、↑の Issue にのように source を指定すると、他のライブラリまで指定した source を参照しようとしていまい、うまく動きません。

[tool.poetry.dependencies]
python = ">=3.9,<3.10"
gokart = "*"
torch = { version = "=1.9.0+cpu", source = "pytorch" }
torchvision = { version = "=0.10.0+cpu", source = "pytorch" }

[[tool.poetry.source]]
name = "pytorch"
url = "https://download.pytorch.org/whl/cpu/"
secondary = true

上記は、そのうまく動かない例です。 gokart をこの PyTorch で指定している url から取ってこようとして 403 が返ってきます。

これはそもそも PyTorch のインストールが PEP 503 に対応したことで、 pipenv の設定がシンプルになるというものがあり、これ poetry でも出来るやんけと思ったら出来なかったという不具合です。

github.com

Downgrading to Poetry 1.0.10 might be a workaround (ontop of my previous comment) as per: python-poetry/poetry#4704 (comment)

Haven't tested because it's too much of a pain, switching to pip!

github.com

しんどいですね。

今回は、最悪ですが whl の url を直接見に行く*1ことで一旦の回避策とします。 こちらですが、 今現在最新の poetry 1.1.12 では、 torchvision が torch の "x.x.x+cpu" に依存しているのにも関わらず 、 '+' 以降がうまく解釈できずに "x.x.x" に依存してるからうまくいかないよと怒られます。 この記事では、さらに苦肉の策として対応されている poetry 1.2.0a2 のプレビュー版を使っています。 誰か私を楽にしてください。

[tool.poetry.dependencies]
python = ">=3.9,<3.10"
gokart = "*"
torch = { url = "https://download.pytorch.org/whl/cpu/torch-1.10.1%2Bcpu-cp39-cp39-linux_x86_64.whl" }
torchvision = { url = "https://download.pytorch.org/whl/cpu/torchvision-0.11.2%2Bcpu-cp39-cp39-linux_x86_64.whl" }

Python のバージョンに依存するので ">=3.9,<3.10" にしてます。

ここまで、書いたら poetry install です。やっと PyTorch が入りましたね。 PyTorch Lightning は poetry が torch と torchvision のバージョンに合わせて依存解決出来るので問題ありません。

書いた

準備が出来たのでサクッとコードを書きました。 特に PyTorch Lightning は初めて書いたので、もっといい感じの書き方があればぜひ教えて下さい。 今回は ResNet で CIFAR-10 データで学習評価まで行うものにしました*2

gokart のタスクは以下の4つに分けてみました。 それぞれサラッと見ていきましょう。

データ前処理

gokart

import gokart
import luigi


class GokartTask(gokart.TaskOnKart):
    task_namespace = 'sansan_adcal_2021'


class PreprocessDataModuleTask(GokartTask):
    _v: int = luigi.IntParameter(default=0)

    def run(self):
        data_module = DataModule()
        data_module.prepare_data()

        self.dump(data_module)

PyTorch Lightning の LightningDataModule

from pathlib import Path

import pytorch_lightning as pl
import torch
from torch.utils.data import DataLoader
from torchvision import datasets, transforms


class DataModule(pl.LightningDataModule):
    def __init__(self, dataset_root_path: Optional[Path] = None, train_size: float = 0.8, seed: int = 1111):
        super().__init__()
        self.dataset_root_path = dataset_root_path
        self.train_size = train_size
        self.seed = seed
        if self.dataset_root_path is None:
            # gokart の中間ファイルの出力に合わせて resources 以下に入れることにする
            self.dataset_root_path = Path(__file__).resolve().parents[2].joinpath('resources', 'dataset')
        self.data_transforms = transforms.Compose([
            transforms.Resize(256),
            transforms.CenterCrop(224),
            transforms.ToTensor(),
            transforms.Normalize(
                mean=[0.485, 0.456, 0.406],
                std=[0.229, 0.224, 0.225]
            ),
        ])

    def prepare_data(self) -> None:
        datasets.CIFAR10(
            root=self.dataset_root_path,
            download=True
        )

    def setup(self, stage: Optional[str] = None) -> None:
        if stage == "fit":
            all_train_dataset = datasets.CIFAR10(
                root=self.dataset_root_path,
                train=True,
                transform=self.data_transforms
            )
            len_train_dataset = int(self.train_size * len(all_train_dataset))
            len_val_dataset = len(all_train_dataset) - len_train_dataset
            self.train_dataset, self.val_dataset = torch.utils.data.random_split(
                all_train_dataset,
                [len_train_dataset, len_val_dataset],
                generator=torch.Generator().manual_seed(self.seed)
            )
        elif stage == "test":
            self.test_dataset = datasets.CIFAR10(
                root=self.dataset_root_path,
                train=False,
                transform=self.data_transforms
            )

    def train_dataloader(self) -> DataLoader:
        return DataLoader(self.train_dataset, batch_size=256, num_workers=8)

    def val_dataloader(self) -> DataLoader:
        return DataLoader(self.val_dataset, batch_size=256, num_workers=8)

    def test_dataloader(self) -> DataLoader:
        return DataLoader(self.test_dataset, batch_size=256, num_workers=8)

定義した DataModule に基本的にデータセットとしての裁量を託しています。 一度だけ呼ぶ prepare_data() メソッドは、このタスク内で呼ぶことにしました。 今回特に定義をしていませんが、前処理に関するパラメータや train, val のデータセットの分割のシード値等を、gokart 側で受け取って DataModule に渡せると良さそうです。

モデル準備

gokart

class PrepareModelTask(GokartTask):
    _v: int = luigi.IntParameter(default=0)

    def run(self):
        model_module = ModelModule()

        self.dump(model_module)

PyTorch Lightning の LightningModule

import torchmetrics
from torchvision.models import resnet34


class ModelModule(pl.LightningModule):
    def __init__(self, *args, **kwargs) -> None:
        super().__init__()

        self.model = resnet34(pretrained=True)
        self.model.fc = torch.nn.Linear(512, 10)
        self.criterion = torch.nn.CrossEntropyLoss()
        self.val_acc = torchmetrics.Accuracy()
        self.test_acc = torchmetrics.Accuracy()

    def configure_optimizers(self):
        optimzier = torch.optim.Adam(self.model.parameters(), lr=1e-3)
        return optimzier

    def forward(self, x: torch.Tensor) -> torch.Tensor:
        return self.model(x)

    def training_step(self, batch, *args, **kwargs) -> torch.Tensor:
        x, y = batch
        pred_y = self.forward(x)

        loss = self.criterion(pred_y, y)
        self.log("train_loss", loss)
        return loss

    def validation_step(self, batch, *args, **kwargs) -> torch.Tensor:
        x, y = batch
        pred_y = self.forward(x)

        loss = self.criterion(pred_y, y)
        self.val_acc(pred_y, y)
        self.log("val_loss", loss)
        self.log("val_acc", self.val_acc, on_step=True, on_epoch=True)
        return loss

    def test_step(self, batch, *args, **kwargs) -> torch.Tensor:
        x, y = batch
        pred_y = self.forward(x)

        loss = self.criterion(pred_y, y)
        self.test_acc(pred_y, y)
        self.log("test_loss", loss)
        self.log("test_acc", self.test_acc, on_step=True, on_epoch=True)

モデルの構築・準備も同様に gokart はタスクの分割と、パラメータの受け渡し口としての使い方が良さそうに感じています。 (データセット同様、今回は何もパラメータを渡してないですが)

モデル訓練

class TrainTask(GokartTask):
    _v: int = luigi.IntParameter(default=1)

    def requires(self):
        return {
            "dataset": PreprocessDataModuleTask(),
            "model": PrepareModelTask()
        }

    def run(self):
        data_module: pl.LightningDataModule = self.load("dataset")
        model_module: pl.LightningModule = self.load("model")

        trainer = pl.Trainer(
            max_epochs=10,
            min_epochs=1
        )

        data_module.setup('fit')
        trainer.fit(model_module, datamodule=data_module)

        self.dump(trainer)

データセットのタスクとモデルのタスクを依存させるようにして、それぞれの LightningModule を渡しています。 各 epoch の checkpoints は PyTorch Lightning 側で持つので、ここを gokart に持たせる必要はないと判断しました。

モデル評価

class EvaluateTask(GokartTask):
    _v: int = luigi.IntParameter(default=0)

    def requires(self):
        return {
            "dataset": PreprocessDataModuleTask(),
            "model": PrepareModelTask(),
            "trainer": TrainTask(),
        }

    def run(self):
        data_module: pl.LightningDataModule = self.load("dataset")
        model_module: pl.LightningModule = self.load("model")
        trainer: pl.Trainer = self.load("trainer")

        data_module.setup("test")
        result = trainer.test(model_module, datamodule=data_module)

        self.dump(result)

Trainer を持ってこさせるようにしています。 評価の処理そのものは LightningModule 側で持っているので、結果を dump() させておいて、後で参照しやすいようにだけしておきました。

まとめと所感

今回は gokart と PyTorch Lightning を組み合わせて、深層学習モデルを動かすサンプルを書いてみました。 深層学習モデル部分の裁量を PyTorch Lightning に持たせ、処理を gokart のタスクで切ることで全体的に処理フローが見えやすい形になったかと思います。 学習のコードがややわかりづらくなりがちな PyTorch のコードは、 PyTorch Lightning で書くことで嫌でも処理がわかりやすくなって良いですね。 また、アドカレとしての締切の時間の都合上、直書きしてしまったパラメータが多いのですが、こういうパラメータはタスクごとに gokart で受け渡せるようにしておくともっと良いかと思います。

コードの全体像は後日まとめて GitHub 上に公開しようと思います。 文章を書いた人間としては、なんだか Poetry 上での torch + torchvision インストールバトルが本題になってしまったようで複雑な気持ちもありますが、許してください。

*1:ここ (https://download.pytorch.org/whl/torch_stable.html) にあります

*2:時間がかかるので、今回は epoch 数等適当に少なめで動作確認だけしました。

whywaitaの発言で遊びたい (GPT-2 でテキスト生成したら失敗しました)

この記事は whywaita Advent Calendar 2021 12日目の記事です。

前日は、id:mizdra さんの

poem.mizdra.net

でした。

?????

もちゔぇ

さて、去年はwhywaitaさんに実際にデータを取るところから協力してもらったにも関わらず遅刻し、いろいろ崩壊したwhywaitaさんの声を生成しました*1。 記事中で軽く注釈で触れましたが、id:tw1sm1k0 さんがテキストデータでwhywaitaさんを生成したので、音声でwhywaitaさんを生成しようと思った次第です。

対話システムでは、「質問」というクエリに対し「回答」になる言語を生成するタスクで、言語生成では最もよく知られたタスクの一つでしょう。 LINE 上で直接 AI と会話が出来ると話題になった「女子高生AIりんな」もこういった言語生成の中で作られたアプリケーションです。

www.rinna.jp

ところで、今年4月、このりんなで使われている言語モデルオープンソースとして公開されました。

prtimes.jp

GPT-2 と RoBERTa のそれぞれ2つのモデルで公開しており、Hugging Face が提供する Transformers で比較的容易に扱うことができます。

huggingface.co

では、whywaitaさんのテキストデータを食わせて遊びましょう (唐突)。

データ

id:tw1sm1k0 さんと同じく、元データとしてwhywaitaさんの tweet データを使います*2

twismik0.hatenablog.com

github.com

なんとなく、2018年の一年分を使うことにします。 この記事のための学習を始めたのが前日12/11の夕方で、特に試行錯誤する余裕はなく、前処理も雑にメンション・URL・ハッシュタグ除去くらいしかしていません。 ツイッターtweet データなんて汚れまくっていて*3、うまく扱えないことが多いので*4こんな雑な前処理はやめましょう。

f:id:nersonu:20211212001145p:plain

モデル

今回は GPT-2 のjapanese-gpt2-medium 事前学習済みモデルとして利用します。 公開されている学習済みモデルを fine-tune することで、いわゆるwhywaita言語モデルを目指します。

f:id:nersonu:20211212004405p:plain:w600
りんなをfine-tuneしてwhywaita言語モデルを作るイメージ図

手順とかコードとかは、やってみた系の記事がいっぱいあると思っているので、そちらを参照すると良いかと思います。 もうデータを Tokenizer で tokenize して、 Transformers の Trainer でいい感じのパラメータ*5を渡して学習させるだけです。

出来たやつを喋らせよう (1年ぶり)

GPT-2 のような言語モデルでテキストを生成させる場合、出だしの文章を与えてあげると、この入力に続くようにテキストを生成します。 比較として今回使ったりんなのモデルもそのまま使ってみて、30文字くらい生成させてみます。 では、早速やってみましょう。

入力「こんにちは、」

  • りんな「こんにちは、今日も暑いですね。 皆さん熱中症には気をつけてください! さて、今回は先」
  • whywaita (学習初期)「こんにちは、ちゃが済のwにuティングガいiphoneなんになってや厳しいba凶悪見てアルコール1動かしgoogleしてしていたコミ」
  • whywaita (学習済み)「こんにちは、部分はでなかったer程学習分のを失いリリーアカウントに色代開催腹期送日をリッシュ見る鑑賞完全に」

入力「久しぶり」

  • りんな「久しぶりに、お会いできて嬉しかったです。ありがとうございました! 今日は、お忙しい中、お」
  • whywaita (学習初期)「久しぶりちゃがの済wにuガティングやいiphoneなんになって厳しい凶悪アルコール見てコミ1bagoogleしてっぽいしていた」
  • whywaita (学習済み)「久しぶり、部分はでなかったはer程分の学習リアカウントリーを失い代色に開催見る腹リッシュ送してを完全に」

入力「オタクは」

  • りんな「オタクは自分の好きなものを好きでいられるのが一番幸せだと思う。 俺もそう思うけど、それって「自分が何」
  • whywaita (学習初期)「オタクはちゃがwい済にのuやっぽいなんガ厳しいiphone見てところ1。がティングするかがすぎるがあるが」
  • whywaita (学習済み)「オタクは部分は、なかったでer程のリ分学習代アカウントを失いリーに送色開催腹日を完全に期鑑賞リッシュ見る」

はい。

おわりに

ということで、酷く過学習しました。

単純に学習データの前処理が不十分なことと、学習方法に問題がありそうです。 今回は面倒くさいので改善もせずに期限に間に合わせるために公開していますが、12月中に改善して記事に出来たらいいなと思います。

明日は id:awawan1123 さんです。 お楽しみに。

*1:https://nersonu.hatenablog.com/entry/whywaita-advent-calendar-2020

*2:一応、whywaitaさんの許可はとりました。

*3:文法とかそういった観点での話です。

*4:あくまでも個人の体感です。

*5:いい感じのパラメータを渡せなかった

24歳になりました。2020年の振り返りと2021年にやりたいこと

2021年1月13日、24歳になりました。

2020年のやったことを振り返って、2021年にやりたいことをまとめたいと思います。

2020年の振り返り

就活とインターンシップ

2020年は就活から始まった記憶が印象深いです。 1月頃はガンガン就活をしていました。 最終的にいくつかの企業様から内定を頂き、3月にそのうちの一社の内定を承諾しました。

また、2020年は2社にインターンシップでお世話になりました。 2月は週2,3回の出社ペースで株式会社リクルート様にお世話になりました。 そのときのことは、ブログの記事にまとめています。

nersonu.hatenablog.com

また、3月の2週間程度、ウォンテッドリー株式会社様にお世話になりました。 レコメンド関連のチームにお世話になり、メンターの方とのディスカッションは非常に有意義なものでした。

あとこのときのインターンシップの報酬でMacBook Airを買いました。

修士2年生

まだ修論の提出も終わっていませんが、2020年度はM2でした。 ただ、このままいくと無事修了できそうです。 良かったですね。 秋辺りに出した国際会議の結果が良くなかったので、追加で別のカンファレンスに出す分で最後さらに追い込まれそうですが、最後まで頑張っていきたい次第です。

あと、M1までやった研究が引き継いでいただいた先輩の手によって2020年内に国際会議に採録されました。 詳しくはこちらにあります。

nersonu.hatenablog.com

在宅と飲酒

今年は新型コロナウイルスであるCOVID-19の影響で、在宅を余儀なくされました。 はじめは在宅で作業が出来る気がしなかったので、思い切って給付金でモニターを買いました。

www.philips.co.jp

4KでType-Cで丁度いいのはどれっ?って聞いたら、Philipsのモニターを紹介されました。 これが無ければ研究は進まなかったでしょう。

また、在宅のストレスで家での飲酒量が増えました。 毎日毎日冷蔵庫にストックした缶ビールをビールグラスに注いで飲んでいました。 止める人間はいません。

漫画をたくさん買った

AmazonKindleの漫画を山ほど買いました。 せっかくなので2020年に買った漫画 (2020年に出版されてない漫画もあるかも、かつ今年買い始めたシリーズ) を少し紹介します。

まくむすび

マンガ大賞2020のノミネート作品です。 高校演劇がテーマの作品で、高校の頃演劇部だった自分には非常に共感できたり思い出すことが多い漫画でした。 まだまだこれからが気になる作品です。

tonarinoyj.jp

ふたりが家族になるまでに

主人公の女の子と従兄の二人生活の距離感が絶妙でした。 あと絵が好み。 こういうドギマギとしてるけど、家族として温かいみたいな話いいですよね。

houbunsha.co.jp

謎の彼女X

今更という作品ではありますが、全巻買ってdアニメストアでアニメも全部観ました。 不思議な繋がりではありつつも、確かにそこに二人の恋愛感情があるのが最高ですね。

afternoon.kodansha.co.jp

よふかしのうた

だがしかしで有名なコトヤマ先生の最新作です。 登場人物のの感情とか、人間関係の話とか、描写が素晴らしくのめり込みます。

websunday.net

イカさんは押しころせない

いいラブコメです。 とりあえず読んでください。

www.akitashoten.co.jp

結婚するって本当ですか

神のみぞ知るセカイ若木民喜先生の最新作です。 社会人ラブコメは好きでいろいろ読んでいるのですが、大好きな先生が描いているので非常に気になっています。 全体的に色々なことへの"気付き"みたいな描写が好きです。

bigcomicbros.net

六畳一間の魔女ライフ

お仕事頑張りコメディ。 頑張ろうっていう気持ちになる作品です。 id:mizdra 君にも気に入ってもらえてよかったです。

magazine.jp.square-enix.com

2021年にやりたいこと

働く

このまま順調に進めば、ありがたいことに働くことになります。 自分が今までやってきた知識/技術を活かして頑張って1年働いていきたい気持ちです。

Kaggleを始める

データを使うぐらいにしか結局Kaggleをやっていなかったので、やってみようかな〜と思っています。 そんなデータ分析のスキルを上げたいとか高尚な理由ではなく、単に面白そうだからです。 今まで精度を競うことにそんなに興味はありませんでしたが、興味が深々津々になってきました。

勉強

今、大学の同期主催の久保川統計の輪講に参加しているのですが、これを踏まえて竹村統計を自分で一冊勉強したいなと思っています。

www.hanmoto.com

復習がてらしっかりと数理統計学の勉強を抑えたいなという気持ちです。

休肝

飲みすぎです。 まずは 週1の休肝 が目標です。

誕生日なので

あとは祝ってください!! 2021年もがんばります!!!!

www.amazon.jp

この時期。

めちゃくちゃscaleboxの記事にアクセスが偏る。 毎年こうなります。

f:id:nersonu:20201222000543p:plain

該当の記事はコレです。

nersonu.hatenablog.com

みんな、TeXで卒論やら修論やら書いていそう。

ぼくも、書いています。

アドベントカレンダーはまだWIPにさせてください。

whywaitaの声で遊びたい (Tacotron2 + WaveGlow で音声合成で遊んだら失敗しました)

この記事は whywaita Advent Calendar 2020 12日目の記事です。

前日はid:Krouton さんの

krazy.tokinia.me

でした。

やっぱり、睡眠は大事ですよね。 ちなみに、私はじぶんまくらの頂を使っています。

jibunmakura.com

モチベーション

さて、whywaitaさんといえばISUCONですよね。 今年は運営側ということでお疲れさまでした。 私は特段参加はしていないのですが、本戦のYouTube Liveを流し見しながら作業をしていると、面白いものが流れてきました。

www.youtube.com

どこかで聞いたことがある声ですね。 ということで、このかっこいい声で遊びたいなと思いました。

データを集める

ところで、何をするにもwhywaitaさんの声のデータが無いと始まりません。 しかし、テキストデータと違って*1、whywaitaさんは自分の音声データを公開していませんでした。 なんてこった。

かといって、YouTubeの動画から音声を引っこ抜くのは、規約を見る感じダメそうです。 困ったなぁ……

f:id:nersonu:20201211232008p:plain:w600

やったぁ!!!

ということで、データが集まりました。 あまり負担をかけるわけにもいかないため、データ数としては124文録音してもらいました。 多分いろいろやるにはデータ数がかなり少ないです。

ちなみに読んでもらった文書は、夏目漱石の『こころ』の一部です。

github.com

青空文庫GitHub上にすべての文書データがあり、便利ですね。

(12/25 追記) 例えば、こんな感じの音声を撮っていただきました。

WIP

さて、本当はこのデータを使ってやりたいことがあったのですが、現状間に合っていません。 この記事のために音声処理を少し勉強したり、論文を読んだりしていましたが、結局修論でそんなに時間が取れませんでした。

ごめんなさい! :bow: :bow: :bow:

正直うまくいかない部分も考慮していたので、このデータを使ってプランBのほうをやろうかなと考えています。 数日以内にこちらのほうをひとまず公開出来たらなと考えております。 許してください〜〜〜

本題 (12/25 更新)

だいぶ遅刻ですね。 もう最終日ですよ。

申し訳ございませんでした! 代表してこちらの方に謝っていただきたいと思います。

なんて言ってるんでしょうね*2

これは何

音声合成を行って、whywaitaさんの声で自由に喋らせようとしましたが、失敗しました。 ひとまず今回は何を行ったのかと、反省点、いくつかの音声サンプルを見せて次回への課題(?)としようと思います。

今回やったことをざっくり話すと……
まず、whywaitaさんが喋った音声とそのテキスト情報を入力として、テキスト情報からwhywaitaさんらしい音声が生成出来るような特徴量の抽出を行っていきます。 これはいわゆる Text to Speech (TTS) と呼ばれているタスクです。 音声処理の知識がほぼ皆無なので雑ですが、具体的にはメルスペクトログラムと呼ばれる音声信号情報から得られる情報を抽出しているようです。

つまりこんな感じかもしれない。

f:id:nersonu:20201225110353p:plain:w500

今回はTacotron2というモデルを使いました。

arxiv.org

著名なモデルで、山程解説があると思うので、気になる方は論文とそちらを読んでみてください。

実装はいろんなところが作っていくつか公開されているので、公開されているものを使っていきます。 今回はNVIDIAの実装を使いました。

github.com

ところで、Tacotron2でその人の声で学習して実際にいい感じの特徴量が得られるまで、24時間ほどの学習データが必要なようです。 困りましたね。

そこで、今回は事前に別の音声で十分に学習されたモデルを使い、これをwhywaitaさんの音声っぽくなるように学習させていく……みたいなことをやっていきます。 事前学習モデルを用いた fine-tune ですね。

ちなみに学習はGoogle ColaboratoryでGPUを拝借して約7時間くらいかかりました*3。 たいへ~ん。

出来たやつを喋らせよう

さて、これでテキスト情報を入力すると、whywaitaさんの声っぽい特徴量を出力していてほしいモデルになったはずですが、ここからさらに音声に変換してもらう必要があります。 音声に変換してくれるモデルのことをボコーダーと呼び、ボコーダーくんが推論して初めてwhywaitaさんっぽい声が出てくる(ハズ)というわけです。

ボコーダーには丁度いいところに事前に学習してあるNVIDIAさんのモデルがあるので、WaveGlowを使います。

arxiv.org

github.com

本来であればこちらも fine-tune したほうがよかったりしなくてもよかったりするらしいですが、今回は時間の都合上カットです。

そして出来た音声がこちらになります。

入力「わたしは、くらうどのしごとをしています。」

入力「うらしまたろうは、かめにのってりゅうぐうじょうへいきました。」

入力「きのうは、おたくとあにくらでぶちあがった。」

入力「どうしてこうなった。」

そうしたかった?

はい。

反省点

なにやらはっきり聞こえたところと、はっきり聞こえなかった単語があったと思います。 例えば、学習データに大量に「わたくし」が含まれているので、過学習しているのか近い音素があると「わたくし」になってしまいました。 データの少なさと多様性の無さがかなり反映されていそうです。

また、入力の際にローマ字に変換しているのですが、こちらも一工夫必要そうです。 今回使った事前に学習されたモデルは(多分)元々英語で学習されているものなので、この辺はうまくやらないといけなさそうですね……*4

おわりに

今回はなんとかwhywaitaさんの声で遊ぶために、頑張って音声合成を行いました。 次回は反省点を踏まえて、whywaitaさんに自然言語を喋らせてデータを取るのはやめようと思います。 なんかどうにかして汎化性能の高い音声特徴量を提供してください。

明日は id:jackson58 さんです。 お楽しみに。

*1:whywaita Advent Calendar 2018 18日目 おいでませ!Chatbot whywaitaくん! - Coyote vs Loadbalancer を思い出す

*2:入力「きじこうしんがおくれてしまい、もうしわけありません。」

*3:バッチサイズ8、エポック数500で、イテレーションが7000いかないくらい回る

*4:参考: Tacotron2系における日本語のunidecodeの不確かさ - Qiita

ベイジアンネットワークに関する研究のまとめ

B4-M1の間は、ベイジアンネットワークに関する研究をやっていました。 途中で信頼できる先輩に引き継いで頂いています。 本記事は、その研究の公開先をまとめたものです。 ちょこちょこ本名を開示しているので今更という感じではありますが、研究業績になるのでバンバン出していきます。 (とりあえず、自分のところに*を付けておきます)

本研究は、ベイジアンネットワーク分類器にモデルアベレージングを導入したものです。 詳しい内容は論文をご覧いただくか、こっそり何かで会った際に聞いてください。

  • 青見樹*・菅原聖太・植野真臣 (2020) アンサンブル学習によるモデル平均ベイジアンネットワーク分類器. 電子情報通信学会論文誌D. Vol.J103-D, No. 3, pp.183--193.

search.ieice.org

  • Shouta Sugahara, Itsuki Aomi* and Maomi Ueno (2020) Bayesian Network Model Averaging Classifiers by Subbagging. Proceedings of Machine Learning Research, (in press).

pgm2020.cs.aau.dk

自分の研究がPMLRにまとめられるの本当か?という気持ちですが、本当のようです。 現在は、自然言語処理領域に近しい研究をしており、またこのような記事が出せると良いなと思っています。 ひとまず、就職する来年3月まではいい感じに研究をやっていきますので、よろしくお願いいたします。

デルアンバサダーに参加して、XPS 13 (9300)を体験しての感想

デルアンバサダープログラムに参加して、1ヶ月ほどXPS 13 (9300)を体験させていただきました。
本記事はその感想です。

使用用途

ざっと、こんな感じです。

過去のXPS 13との比較

自分はDellXPS 13が個人的に好きなので、持っているXPS 13と比べました。

ステッカーがはちゃめちゃで申し訳ないのですが、左から2015年の"XPS 13 (9340)"、2019年の"XPS 13 (9380)"、そして今回の"XPS 13 (9300)"となっています。 全体的に、薄く軽くなりました。XPS 13 (9380)を使っているときからだいぶ軽くなったなと感じていましたが、今回のモデルのほうが重さとしてはかなり気にならないレベルまでいったかなというのが体感です。 普段iPadと専門書がリュックサックに入ったりするので、少しでも軽いと助かりますね。

XPS 13 (9380)との比較としては、カメラの位置と電源ボタン(指紋認証センサー)の位置がまず大きな違いかなと思います。

カメラとキーボード

XPS 13はやはり最大のネックがモニタ下部のインカメラと言われていましたが、直近のモデルではモニタ上部に付いており、変なカメラ角度が気にならなくなって良いですね。 それでいてベゼルレスを実現しているのが流石です。 やはり、XPS 13の魅力はベゼルレスで小さいのに大きく見えるところですね〜〜〜

f:id:nersonu:20200721001316j:plain

電源ボタンの位置は、正直誤って押してしまうのではないかと危惧していましたが、そこは問題なさそうです。 なんとなくBackSpace上部に付いていると、押してしまいそうですがMacBook Airを使っているとき同様、気になりません。

それよりも気になってしまったのが、左端のTab, CapsLock, Shiftの長さです。 少し長いかなと感じていましたが、個人的にはかなりミスタイプを連発していました。 あまり好きではないところです。

打鍵感も好みが分かれるところかなと思います。 頑張って言葉で表現すると、クッション感があり、柔らかく感じる打鍵感です。 上質な感じがするといってしまえばそうですが、XPS 13 (9340), XPS 13 (9380)の少し安っぽくカチャカチャした感じはするけども、キーが沈む分軽くて打ちやすいといった感触が消えていて、個人的にはマイナスポイントに思います。 どうしても、作業内容としてコーディングが大半を占めるので、この辺は嫌でも気になってしまうところだと思います。

モニタ

内蔵モニタのタッチパネルは正直使うことはないです。 指紋で汚れるのも嫌ですしね… XPS 13 (9380)を買うときも、解像度は4K欲しいがタッチパネルはいらないなぁとなりました。 ココらへんなんとか、抜きになりませんか? このタッチパネル付きのモニタだと、液晶の光の反射が酷いんですよね。

f:id:nersonu:20200721001345j:plain

汚い部屋が反射して映ってしまっていますが、明るい場所でダークテーマのエディタでコーディングするのは正直キツかったです。 これはあまりXPS 13 (9380)のときから、というかタッチパネル付きの液晶のところで全く改善されていないところですね。 大きなマイナスポイントです。

性能

今回、Ubuntuデュアルブートでぶっこもうとしたら、Ubuntuインストーラパーティションの設定をしようとしたときになぜかエラーで弾かれてしまいました…… 本機から、今まで未対応だったUbuntuでも指紋認証が使えるという大きなメリットも有るそうで、これが試せなかったのが一番残念です。 正直今回のモニタ企画を申し込んだときもここを確認したかったので、不完全燃焼感がすごいです……

XPS 13 (9380)はWindows 10 + Ubuntu 18.04 のデュアルブートで使用していて、出来る限り環境を揃えて実験してみたかったのですが、叶わず残念です*1。 ということで体感ですが、正直Webブラウジングや動画視聴程度ではXPS 13 (9380)との大きな差は感じられませんでした。 手元のプログラムをWSL上で動かした感じだと、CPUの性能が上がっているので実行速度は上がっていると思いますが、コーディング作業等の作業効率の改善率としては差がわかりません。 これは読者の課題とします。

こういうポイントは特に、大学のレポートや、仕事、ゲーム等で使っている他のデルアンバサダー参加者のレポートのほうが役に立つと思います。 ハッシュタグ「#デルアンバサダー」で調べてみてください。

総評

全体的なバランスは良いものの、性能面や細かいところで今までと比べるとインパクトは薄いかなと感じてしまいました。 ただ、このスペックでのコスパは最強だと思うので、Linuxを入れて開発機として使うならばやはりこのシリーズは買いだと思います。 正直Windowsで使うメリットはよくわかりません。 日本でもメモリをさらにマシマシして、Ubuntu標準搭載の開発用モデルを売って欲しいなと、触っていてつくづく思った次第です。 Dellさんの今後に期待していきましょう。

おまけ

これは本当になんですか???

*1:わざわざ事務局の方に問い合わせて、許可まで頂いたのにどうして