nikkie-ftnextの日記

イベントレポートや読書メモを発信

DVCでデータバージョニング 素振りの記

はじめに

アイの歌声を聴かせて、冒頭17分だけでもみんな観て! nikkieです。

さて、最近、Data Science Version Control(以下、DVC)というツールがあることを知りました。
DVCでできることはたくさんあるようですが、まずは一歩目としてDVCによる、データサイエンスで扱うファイルのバージョン管理の素振りをまとめます。

目次

この記事のまとめ

以下のドキュメントやYouTube動画に沿って手を動かしています。

素振り内容

  • DVCを使ってローカルマシンとリモートストレージでデータを同期
  • DVCでデータのバージョン管理

DVCとは

iterative.ai社による「Open-source Version Control System for Machine Learning Projects」(機械学習プロジェクト向けのオープンソースバージョン管理システム)を謳うツールです。

機械学習プロジェクトでは、データやモデルといった大容量のファイルを扱います。
大きなファイルはGitでは管理しづらいのですが、Gitと合わせてDVCを使うことでバージョン管理できます!
大きなファイルの管理はDVCが担当し、言わば Gitを拡張 してくれます(どうやるかはこの後をお楽しみに)

DVCを使うことで私たちは、大きなデータセットのようなGitの外側に置いておきたいファイルにまでGitによるバージョン管理を拡張できる(動画 10:251 意訳)

DVCのインストール

DVCはPythonで実装されており、pipでインストールできます2

pip install dvc[gdrive]

リモートストレージごとに必要な依存関係を含めてインストールしました。
今回はGoogleドライブを使っていきます。

you might need to install optional dependencies: [s3], [gdrive], [gs], [azure], [ssh], [hdfs], [webdav], [oss]. (引用元

動作環境

DVCでデータバージョニング♪

初期設定の後

  • ローカルマシンとリモートストレージでデータを同期
  • DVCでデータのバージョン管理

の素振りをしていきます。

初期設定

git init
dvc init

隠しディレクトリ.dvc(とその中のファイル)と隠しファイル.dvcignoreができます3

  • .dvc/.gitignore (.dvc下のキャッシュディレクトリや一時ディレクトリを無視)
  • .dvc/config (この時点では空)
  • .dvcignore (この素振りでは扱いません)

できたファイルをgit commitして進みます。

mkdir data

以下では、機械学習プロジェクトで使う大容量ファイルをdataディレクトリに置きます。

ローカルマシンとリモートストレージでデータを同期

まずローカルマシンにデータを取得します。

dvc get https://github.com/iterative/dataset-registry get-started/data.xml -o data/data.xml

data/data.xml (14MB) のファイルが取得されます。
サイズは小さいと思うかもしれませんが、これを「機械学習プロジェクトで使う大容量ファイル」とみなして進めます。

大容量ファイルをGitではなくDVCで管理しましょう。

dvc add data/data.xml

2つのファイルができます。

  • data/.gitignore (Gitにはdata/data.xmlを無視させます)
  • data/data.xml.dvc (DVCがファイルの管理に使うメタデータ

この2つのファイルをgit commitします。

続いてGoogleドライブ(リモートストレージ)と同期しましょう。
Googleドライブ(マイドライブ以下)にフォルダを作り、URLを控えます。
https://drive.google.com/drive/folders/{ID}
URLのID部分を使って、DVCを設定します。

dvc remote add -d storage gdrive://{ID}

-dはデフォルトのリモートにする設定とのことです。
この設定により.dvc/configが変更されます。
これをgit commitしましょう。

リモートストレージの指定をしたら、データをリモートに同期しましょう。

dvc push

初回は、Googleドライブの認証・認可が必要になります。
ターミナルの出力に従い、表示されたAuthorization codeを入力すれば済みます4

ローカルのデータを消しても、リモートからpullできます5

rm data/data.xml
dvc pull

どうやら.dvc/cacheの中身とGoogleドライブが同期しているようです。

DVCでデータのバージョン管理

data/data.xmlの容量を倍にします(14MB -> 28MB)。

cp data/data.xml /tmp/
cat /tmp/data.xml >> data/data.xml

データの変更をDVCで追跡します。

dvc add data/data.xml

メタデータdata/data.xml.dvcに記録されたファイルサイズとハッシュが書き変わりました。
このファイルはGit管理しているので、Gitにコミットを作ります。

変更したデータをリモートストレージと同期しましょう。

dvc push

リモートストレージやローカルのキャッシュディレクトリには14MBのデータと28MBのデータ、両方が格納されます。
続く操作でdata/data.xml.dvcを変更し、データを取り出します。

最後にdata/data.xml14MBに戻しましょう。

まずdata/data.xml.dvcを1つ前のコミットに戻します (最新のコミットはdata/data.xmlを28MBに変えた後にdvc addした内容になっているため)。

git checkout HEAD^1 data/data.xml.dvc

メタデータdata/data.xml.dvcは14MBに戻りました。
データを戻しましょう。

dvc checkout

data/data.xmlのサイズを見ると、14MBに戻っています!

このデータを14MBで扱っていきたければ、戻したdata/data.xml.dvcgit commitします。

今回はデータをバージョン管理しましたが、データだけに限らず、モデルのファイル(pickleなど)も同様にDVCで管理できそうです!

終わりに

DVC、なかなか好感触です!
機械学習の実験を回しているとき「データ上書きしたら」って注意を払っているんですが、DVCに管理を任せると「上書きしても戻せばいいや」って前のめりにいけそうですね。
また、dvc addなどでデータのバージョン管理だけを導入できる(小さく始められる)のもよさそうです。
慣れたらパイプライン導入(おまけ参照)と進めていけそうです(バージョン管理だけ試していまいちなら引き返せる)。

おまけ:DVCで他にもできること

ここで示したデータのバージョン管理の他にも、機械学習プロジェクトを捗らせそうな機能があるようです!

パイプラインの定義・実行(dvc repro

DVCでDAGが定義でき(yamlで表現)、例えば「データ分割」「特徴量抽出」「モデル訓練」とスクリプトを順に実行させられるようです。
実行結果とパラメタはキャッシュされるので、「モデル訓練」だけパラメタを変えてパイプラインを実行できそう、捗りそう!
(AirflowやPrefectなどのワークフローエンジンのポジションをDVCも担えるってことですかね。モデル訓練でhydraなどは組合せられるのかな?)

実験管理(dvc exp

パイプラインを使って実験を回す例がYouTubeに見つかりました。
その後にdvc expがコマンドに加わったようで、こちらは積ん読です(さらに便利になっているといいな)。


  1. 文字起こしによると「and dvc is helping us extend git version control to files that we want to keep outside of git things like a big data set」

  2. https://dvc.org/doc/install#install-as-a-python-library なお、macOSbrewLinuxのパッケージリポジトリからのインストールもできます

  3. 匿名化された使用情報のオプトアウトも案内されます。オプトアウトするやり方は https://dvc.org/doc/user-guide/analytics#opting-out を参照

  4. 依存関係にPyDrive2がありました。認証・認可に関してGoogleドライブの操作は https://qiita.com/ftnext/items/60ced8bc432bec6101f0#2-%E5%8B%95%E4%BD%9C%E7%A2%BA%E8%AA%8D が参考になるかもしれません(差分:Googleドライブで許可した後に表示されるAuthorization codeをターミナルに入力する必要があります)

  5. 動画にならってrm -r .dvc/cacheとキャッシュの中まで消したところ、「failed to pull data from the cloud」とエラーになりました。これはIssueなどを確認してみたいですね。再度dvc getして事なきを得ました(ファイル自体は変わらないため)

dataclassデコレータを使ったクラスが #ミノ駆動本 でいう「データクラス」になるかは、プログラマ次第

はじめに

パチャンガ乗りたい!1 nikkieです。

最近は、主催者の1人としてミノ駆動本_読書pyを主催しています2
これまでに1,2章の回、3章の回と2回開催しました。

読書会での学びをアウトプットしていきます。
まずはPythondataclassデコレータに関して数回予定しています。

目次

まとめ:現時点のnikkieの考え

  • Python@dataclassを使うことはアンチパターン「データクラス」を作ることには ならない
  • ただし、アンチパターン「データクラス」を知らないと、気づかぬうちに作っていることもありえる
  • アンチパターン「データクラス」を知った上で積極的に@dataclassを使おう!
    • @dataclassはクラス定義の記述量を減らす、「シンタックスシュガー的なもの」と捉えています
    • 書くコード量が減るので、データを計算するロジックをがっつり書くのに注力できる!

設計入門本における「データクラス」

「データクラス」は、アンチパターンとして語られています。

ミノ駆動本

『良いコード/悪いコードで学ぶ設計入門』第1章 1.3「さまざまな悪魔を招きやすいデータクラス」にて何が悪いのか詳説されています。

データクラスは、設計が不十分なソフトウェアで頻繁に登場するクラス構造です。(p.32)

データを保持するクラス=データクラスと、データを使って計算するロジックが分散し、低凝集なコードになってしまいます。

招く悪魔はこんなにあります:

  • 重複コード(ロジックが重複)
  • 修正漏れ
  • 可読性低下
  • 生焼けオブジェクト(未初期化状態が発生しうる)
  • 不正値の混入

増田本(『現場で役立つシステム設計の原則』)

「データクラス」という語は『現場で役立つシステム設計の原則』の3章にも登場します(読書会で増田さんの本にも書いてあると教えていただきました。ありがとうございます!)

このデータクラスは、Javaの文法的には何もまちがっていません。しかし、オブジェクト指向のクラスの使い方としては、まちがっています。(Kindle の位置No.1216-1217)

このようなロジックを持たないクラスは、本来のクラスではありません。(Kindle の位置No.1218-1219)

引用した部分以外にも「なぜデータクラスが作られるのか」という考察もあるので、興味を持った方は読んでみてください。
私はスッキリしましたー

アンチパターン「データクラス」とは

  • データだけを持ち、ロジックを持たないクラス
  • オブジェクト指向のクラスの使い方として誤り

Pythonにおけるdata class

Python 3.7で標準ライブラリに加わりました。

ドキュメント

提案したPEP

@dataclassでクラスをデコレートすると

ドキュメントやPEPにある例ですが、__init__が自動で生えます(説明はドキュメントの冒頭にあります)。

from dataclasses import dataclass

@dataclass
class InventoryItem:
    """商品明細のアイテムの最新の状態を把握するためのクラス"""
    name: str
    unit_price: float
    quantity_on_hand: int = 0

    def total_cost(self) -> float:
        return self.unit_price * self.quantity_on_hand

InventoryItemクラスのインスタンスを1つ作ってみます。

$ python3.10 -i dataclass_example.py  # クラス定義をスクリプトに書いて読み込む
>>> item = InventoryItem("Awesome item", 10000)
>>> item
InventoryItem(name='Awesome item', unit_price=10000, quantity_on_hand=0)

インスタンス作成時にnameunit_priceを指定し、quantity_on_handはデフォルト値を指定しました。

__init__のほか__repr__も自動で生えるので、対話モードでの表示が分かりやすいですね。

@dataclassでデコレートしたクラスをアンチパターン「データクラス」にしないために

上記例を見ていただくと、total_costメソッドが定義されています。
これは合計コストを計算するロジックであり、このInventoryItemクラスはデータクラスではないですね。
「この例はよくできているなー」と思っていて、@dataclassを使ったクラスにロジックも持たせているわけです。

アンチパターン「データクラス」は、データだけを持ち、ロジックを持たないクラスでした。
@dataclassを使い、さらにそのクラスにロジックも実装することで、アンチパターン「データクラス」は回避できます!

@dataclassを使うメリット

使わないときと比べてコードの記述量が減る、これに尽きます。

インスタンスが持つ属性の名前と型を書くだけで、実行時に自動的に__init__を持ちます。
__init__を持つので、作ったインスタンスが生焼けになることもありません。

@dataclassを使ってミノ駆動本で紹介された設計を実装できると考えていますが、それは別の記事でアウトプットすることにします。

終わりに

この記事は以下の返信へのアンサー記事でもあります。

ミノ駆動本_読書pyでは「Pythonで実装するならこうかな」というアウトプットにフィードバックをいただき、自分の理解も深まってなかなか刺激的です。
気になるところを読んでいく形ですので、興味がある箇所の回がありましたら、お気軽にご参加ください。
次回は条件分岐の中の6.2(switch文の重複)です!



  1. であいもん最終話、この一果ちゃんかわいかったー。和さんの妄想ですが

  2. ミノ駆動本=『良いコード/悪いコードで学ぶ設計入門』。これまでにこんな記事も書いています:

今の自然言語処理AIは #アイの歌声を聴かせて のシオンとどれくらい近いのか、感情分析機能を比較してみました

はじめに

こんな景色、ずっと見たかった。"公開直後"からずっと!

吉田さん、興津さん、生コメンタリーラジオ みんなでアイうた ありがとうございました!

実は今の自然言語処理AIに あるお願い をして、みんなでアイうた に参加していました。
この記事にまとめます。

目次

2022年時点の自然言語処理AIへのお願い

今の自然言語処理AIには、日本語の文章がポジティブかネガティブか判定できるものがあります(このタスクは感情分析や極性判定と言われます)。

このAIに『アイの歌声を聴かせて』のサトミのセリフを入れたら、サトミの感情(ポジティブ/ネガティブ)を判定できるってことですね!!
今のAIでどのくらい感情を判定できるのか(シオンとどれくらい一致するのか)私気になります!

ポジティブ/ネガティブと幸せか否かは、以下の仮説で対応させることにしました:

  • サトミのセリフがポジティブならサトミは幸せ
  • サトミのセリフがネガティブならサトミは幸せでない

感情分析、わずか数行!

上記のAI(daigo/bert-base-japanese-sentiment)を使うには、Pythonでは以下のようなコードになります:

from transformers import pipeline

nlp = pipeline(
    "sentiment-analysis",
    model="daigo/bert-base-japanese-sentiment",
    tokenizer="daigo/bert-base-japanese-sentiment",
    truncation=True,
)

python -i awesome.py で実行した後、nlpに文字列を渡すと感情分析されます!

>>> nlp("吾輩は猫である")
[{'label': 'ポジティブ', 'score': 0.9805924892425537}]

スコアについて

上のスコアですが、ポジティブ+ネガティブ=1になると理解しています。

  • 1に近いとポジティブ/ネガティブ自信あり
  • 0.5に近いとポジティブかネガティブか自信なし

ポジティブ+ネガティブ=1の挙動は、以下のサイトでも確認できます(Hosted inference API)。

結果発表! ポンコツ具合がシオンと近い?

それでは現在の日本語感情分析AIは、サトミがシオンに言ったセリフをどのように判定したのでしょうか?
果たしてシオンになれるのか、チャレンジスタート!

転校直後のセリフ、シオンはネガティブとして受け取っていると思う1ので、ちょっとイマイチ。
「歌うの禁止!」はネガティブだと判定してほしい!

1日目の音楽室(ユー・ニード・ア・フレンドを歌う前)。
このセリフはネガティブだと思うのですが、ポジティブと受け取ったから、そのまま歌い出したんですかね?
ここはポンコツ具合がシオンと一致しているかも!

サトミを幸せにするためにピンチ施策を試した後。
施策を試した後のサトミからのフィードバックはネガティブ(幸せになっていない)。
だから次の施策に行ったのか!

行動に対してのこのフィードバック、人間だったら結構なダメージだ、これ

※この記事ではアイうたのネタバレしないので、この後に続くサトミ→シオンのセリフに興味のある向きは、Twitterスレッドを直接ご覧ください

やってみての気付き

思わぬ気付きがあったんですよ!

サトミ→シオンのセリフをポジティブ(=サトミは幸せ)と判定したら応じればよさそう。
逆に(あんまりなさそうですが)ネガティブと判定したら断るのかなと思います。

そして、シオンはセリフを文字ではなく音声で受け取っているんですよね。
なので、文字だけでなく抑揚など、感情分析に有用な特徴量が入力されていそうです2
例えば「歌うの禁止!」も語気が強いですもんね。
日本語テキストからの感情分析を発展させて、日本語音声からの感情分析、できないかなあ(論文とかあるのかなあ?)

終わりに

このアイデア(電波)は、6/9のみんなのPython勉強会で「BERT以後の自然言語処理入門」を話す準備をしている中で浮かびました!
6/9の発表準備はかなり苦労していて、発表が終わって10日にぶっつけ本番で試してみました(いやもうテンション上がっちゃって)

やってみたらポンコツな部分も機能している部分も似ていたという感想です3
未来は意外と近くにある!
考察的な気付きもあり、手を動かしてよかったなーと思います。

現実にはdaigo/bert-base-japanese-sentimentのようなAIは誰でも使えるのですが、果たしてシオン開発ではそういうAIを使っているんでしょうか?
こういうところも気になります!

動作環境

  • IntelチップのmacOS(M1ではないということです)
    • CPUで検証しています
  • Python 3.9.4
  • torch 1.11.0
  • transformers 4.19.2
    • pip install torch transformers[ja] で環境構築しました

  1. 監督のトークからここでシオンは歌うの禁止を学習していると理解しています

  2. さらに言えば、シオンは音声だけでなく視覚情報も使ってますよね(マルチモーダル)

  3. シオンのソフトウェアとハードウェアは同じタイミングで完成していなくても辻褄は合うので、自然言語処理AIとしてはひょっとするとほとんど変わらないのかもしれません。魔法は他のソフトウェアやハードウェアなのかも!

#ミノ駆動本 2章を読んで、クラスの使い所がようやく分かった気がします

はじめに

こんな景色、ずっと見たかった。nikkieです。

いやー、『良いコード/悪いコードで学ぶ設計入門』(以下ミノ駆動本)、売れてますね!
この記事はミノ駆動本について以下2点をアウトプットします:

  • ミノ駆動本 2章のアウトプット
  • ミノ駆動本 読書会のお知らせ

目次

ずっと思ってきた、クラスってどこで使うの?

プログラミングは大学の授業からなのですが、ソフトウェアエンジニアとして働いている中でも、クラスの使い所がかなり長いこと分からなかったんです。

入門書でクラスの文法を説明するために登場する例を見て、文法は分かるのですが、じゃあどんなシーンでクラスを使うのか、関数よりもクラスを選ぶ理由は何なのか、私があたってきた教材からは掴みかねていました。

例えば、Pythonチュートリアルでは犬の例があります。
https://docs.python.org/ja/3/tutorial/classes.html#class-and-instance-variables
ただ、私の普段のプログラミングに犬は出てこないんですよ1
「クラスってどんなシーンで使うのーー?」

言い換えると、関数で書くのが向かないシーンがあり、そこでクラスが使われると思うのですが、「じゃあそのシーンって具体的にどんなシーンなの?」というのが、犬クラスなどの文法説明の例からピンとこなかったわけです。

使い所が分からずクラスについては苦手意識があったのですが、その中で、犬(や自動車)のような具体的なものだけでなく、処理もクラスで表せる!という気付きがありました(上のスライド2でアウトプットしています)。
この気付きでようやく「クラスの使いどころちょっとだけは分かったかも」という感じでした(処理を表したクラスに属性をもたせることで、関数だったら何回も渡していた引数がなくなってスッキリし、「ちょっといいかも」と思えたんですよね)。

ミノ駆動本 2章は明確なクラスの例を示した

2.4にHitPointクラスが登場します。
HitPointに関係するロジックをHitPointクラスにまとめています

  • ダメージを受けるメソッド
  • 回復するメソッド

強く関係し合うデータとロジックを一箇所にギュッと集めておく (p.51)

Pythonで書いてみた実装はこちら:

この例を見て初めて「クラスってこうやって使うんだ」と腹落ちしました。

2章は2.4に向かう構成も分かりやすいです。
ベタ書きの処理を

  • 2.1 命名(省略しない)
  • 2.2 再代入しない
  • 2.3 メソッド化(ロジックを整理)

カイゼンしていきます。
そして、2.4でデータとロジックをまとめたクラスの例をHitPointクラスで示すのです。

ベタ書きの処理をカイゼンしていき、それがクラスにもつながるというのは、もっと早く読みたかった!ですね

2.4のクラスの例はまだ序の口です。
続きを読んだり、Pythonでどう実装するかを(読書会の機会も使って)考えたりしていきたいと思っています。

ミノ駆動本 読書会やりまーす!📣

「1人で読めばいいんだし」と思っていましたが、10人を超える参加者が集まりました!
参加ありがとうございます。
また、ツイートへの反応や拡散も誠にありがとうございます。

初回は 6/3(金) 19:30〜 です。
まだ参加申込できますよー

読書会準備の裏話:命名

命名に込めた意図を共有します(本邦初公開!)

  • Pythonに関係する読書会」を表したかったので、読書py(ドクショパイ)としました
  • ミノ駆動本の読書pyですが、「Pythonならsnake caseだよね」ってことで「ミノ駆動本_読書py」となります
  • 何回か開催する予定なのですが、「番号表記はPythonシーケンスっぽくしよう」とミノ駆動本_読書py[0]という命名3となりました4
    • 初回を表すべく、最初のインデックスの0としています

終わりに

もし私と同じ「クラスってどんなシーンで使うの?」という疑問を抱えている方がいたら、ミノ駆動本2章を読んでみてください5
1章・2章は新卒プログラマさん向けというのもあり、読みやすく感じました。


  1. 入門書のクラスの例が文法説明のためだけの例になりがち問題は、プログラミング学習に関するイシューのようにも見えます。解決すると幸せになる方はそこそこいるんじゃないでしょうか?

  2. 2021年11月のオブジェクト指向LTでアウトプットしたものです。

  3. ミノ駆動本_読書pyは変数名で何らかのシーケンスを指していると思われます

  4. シーケンスとは「長さがあり、整数インデクスで要素にアクセスできるオブジェクト」です。ref: https://docs.python.org/ja/3/glossary.html#term-sequence

  5. 読書pyに来ていただいてもかまいません

近況報告:『The Art of Agile Development Second edition』の読書会に参加しています #agile_devs

はじめに

秘密はね、最後に明かされるんだよ、nikkieです。

今回は近況報告として、秘密(※過言)を明かします。
最近参加している、アジャイル開発本の読書会について共有です。

目次

『The Art of Agile Development Second edition』

James Shore氏による、アジャイル開発(Agile Development)についての本です。

初版は邦訳もされています。

本書は、アジャイル開発の実践方法の1つであるXPを中心に解説します。

第2版は昨年秋ごろに出ました🎉
未邦訳なので英語で読んでいます。
価格はお高めでしたが、「アジャイル開発、特にXPについて知見を得られるなら実質無料では」(※過言です)とKindle版を買っちった!

nikkieとアジャイル開発

この春でソフトウェア開発に携わるキャリアは7年目に突入しました。

ペアプロ、TDD、ユーザストーリーなどXPのプラクティスは一通り体験しています。
慣れてきたからか「私が理解しているXPって、ホントにこれでいいんだっけ」とたびたび思うようになり、『The Art of Agile Development』にも手が伸びました。
経験豊富な実践者(著者)からXPの知見を学んで、自分の知識を相対化したいと思ったのです。

アートオブアジャイルデベロップメント第2版 読書会

現在は隔週月曜日の19:30〜22:00でやっています。

  • 19:30〜20:00 事前ワーク時間(もくもく会
    • 私はそんなに早く英語が読めないので、前日に時間を取って読むことが多いです
    • 英語で読むのは必須ではなく、DeepLなどで翻訳して読む参加者が多いようです
  • 20:00〜22:00 読書会本編(続くスライドをどうぞ!)

スライドにもありますが、Agileの経験がなくても参加できます

読書会の時間はDiscordに集まります(アクセス方法はconnpass参照!)。
ボイスチャットをつなぎ、その回の担当者(複数名。持ち回り)が話します。
話題はもくもく会で書き出してあり、参加者全員で投票して、票を集めたものから話します。
話している間は、テキストチャットで感想や質問を投稿できますし(ボイスチャットに拾ってもらえます!)、マイクをオンにして直接話しても大丈夫です!

これまでに扱ったのはこちら:

読書会はいいぞ

そもそもこの読書会に参加したのは、ペアプログラミングについて、もやもやがあったからでした。
ペアプロでは自分1人では思いつかなかったような設計・実装に至れることがあり、それが楽しく感じる2のですが、「2人の間にスキル差があるときその状態は可能なのか。そして、それはどのようにすれば実現できるのか」ともやもやしていました。
@jnuank_さんから初回の読書会の存在を教えてもらい、かつ、ペアプログラミングの章だったので、「ちょっとしたことでもヒントになるかも!」と英語版をポチって参加しました。
実際書籍からも、読書会中の話からも学びは多く3、スキル差のあるペアプロについてもいくつかアイデアを試せています。

初回で「Zero Frictionという章が気になる」とチャットしたら、無料公開されていたこともあり、2回目のトピックに決まり、続けて参加しました。
2回連続で参加して自分の中でリズムができたのもあって継続して参加しています。

英語自体は平易ですが、私の経験不足で意味が取れない箇所もあります。
そんな箇所を読書会に持ち寄ると、他の方の視点や知識ですんなり理解できたという経験をこれまでに何度もしています。
参加者みんなで読んでいる感覚が味わえて、「読書会はいいぞ」って感じですね。

また、この読書会で本家のBook Clubの存在を知り、Martin Fowler氏やKent Beck氏からZoomで直で話(英語)を聞く超刺激的な機会4にも出会えました!

終わりに

XPについての自分の知識を相対化したく、最近は『The Art of Agile Development Second edition』を読んだり、読書会に参加したりしています。

次回読書会は5/23(月) 19:30〜です。

この記事で興味を持ったよという方がいたら、ぜひぜひ気軽にお越しくださーい!(ウェルカムカモーン)

隔週で読書会に参加するというのは、PyCon JPスタッフ活動の"休職"状態を選んだからこそできているなーと思います(詳しくはまたの機会に)

追伸:アジャイル・XP関連のアウトプットの道

これまでの私のアウトプットはPythonが中心でしたが、XPもアウトプットのネタになるかもと感じ始めています。
今後このブログでアウトプットしたり、

こんなLT会にエントリしてみたり(ゆるくなのでアウトプット踏み出しやすそう!)、

XP祭りにプロポーザル出してみたりしてもいいかも、なんて妄想しています。


  1. 1時間デプロイに関係する話で、刺激的でした

  2. 直近のPythonイベント紹介(二〇二二 卯月) ※Python以外もあります - nikkie-ftnextの日記でも紹介しています。アーカイブYouTubeに上がると思います

直近のPythonイベント紹介(二〇二二 皐月) ※Python以外もあります

はじめに

「今夜、私が(話を聴かせて)いただくのは」、nikkieです。

みんなのPython勉強会の「直近のPythonイベント紹介」向けにまとめた勉強会コレクション 2022/05版です。

前回

目次

前提1

  • nikkieが気になっている勉強会の情報(nikkie's choice)です(参加予定表ではありません)
    • 見えている範囲でカンファレンス登壇視点での情報も共有します
  • 読んだ方が、新しい勉強会を知ったり、参加して学びがあったり、人と繋がったりしたらいいなという願いを込めて記事にしています
    • nikkieの手元には、勉強会の情報が溢れかえっています
    • 「この状態は他の方にとっては当然ではないかも。どんな勉強会があるかよくわからない方もいるのでは」という気付きがこのシリーズのモチベーションです
  • 網羅するより推し!というスタンスです
    • あなたのオススメ勉強会があれば、Twitter @ftnextまでお知らせください

それでは今回も行ってみましょう!

5/14~15 SRE NEXT 2022 ONLINE

午後半日開催×2日間!
私はSRE領域は専門外ですが、機械学習関連のトークだったり、面識のある方のトーク2があったりするので、部分参加してみようかなと心惹かれてます。

5/18(以降の毎週水曜日) 10分DDD

毎週開催!!🤩
とても楽しみです!

5/20 (19時〜) DDD Meetup! ドメイン駆動設計をちゅらちゅら語る会

LTの猛者が集まる(とnikkieが勝手に思っている)ちゅらデータさんのLT会、5月はDDD!

5/20 (21時〜) はんなりPython #49 ライブラリ研究会

5/20はもう1件!
はしごできる時間帯ですね(19時→21時)。

ライブラリ研究、結構興味あります👀

5/25 Python Tips LT会 - vol.3 🎤

ラクスさん開催のLT会、テーマはPython Tips。
***」についてLTする🙋‍♂️ので、みんな来て!(宣伝)

5/26 サーバーレス LT vol.2 🎤

こちらでもLT登壇します🙋‍♂️。
アイの歌声を聴かせて』にまつわる開発の1つ、AWS Lambda製Twitter Bot🤖についてアウトプット予定です!

5/27 Baseball Play Study mini〜野球AIで推し選手の成績を占おう

Python界隈では"野球の人"として知られるshinyorkeさん登壇回!
私は野球とは全然縁がないですが、「プロ野球の成績予測エンジン」の事例紹介ということで、機械学習観点(モデル作成やMLOps)から興味あります。

5/31 AWS Developer Live Show ドメイン駆動設計のススメ

AWSさん、月1でYouTube Live始められたんですね!(アーカイブあり)

6/9 みんなのPython勉強会#82

現在企画中です(NLP回の予定)。
「秘密はね、最後に明かされる」かも?

カンファレンス登壇に関する情報

PyCon JP 2022 プロポーザルは6/13(月)20:59まで!

プロポーザル投稿はこちらから!

登壇の一歩目に、ラクスさんのLT会がすごいです!

たびたびLT登壇しているラクスさんのLT会シリーズですが、多岐にわたるテーマが用意されていて、アウトプットの素振りにすごくよさそうです。

ラクス - connpass を見ていただきたいのですが、ラインナップは

「どの回も登壇できるネタ持ってる!でも開発時間も取りたいし…」とまさか登壇をセーブする状況になるとは思いもよりませんでした。
ヒトツダケナンテエラベナイヨー(悩🤯)

終わりに

2022年5月〜6月の気になっている勉強会リストとカンファレンスについて共有しました。
これを読んで、「この勉強会興味持った!」というのが1つでもあったら嬉しいです。
「私もこれ、気になる!」という勉強会があれば、ぜひ「今夜、私が(話を聴かせて)いただくのは」しましょう!


  1. 直近のPythonイベント紹介(二〇二二 弥生) ※Python以外もあります - nikkie-ftnextの日記 の開始時の前提からは変わらず、微アップデートを加えています

『Transformerによる自然言語処理』のRoBERTa事前訓練のコードを、データをhuggingface/datasetsで読み込むように書き直す

はじめに

今日も素振りにとりくーみこ!1 nikkieです!

先日、『Transformerによる自然言語処理』の中のRoBERTaの事前訓練を写経したという記事を書きました:

"考えながら写経"していて、いくつか掘り下げたい事項が出てきています。
今回はデータの読み込みにフォーカスします。

目次

今回解消する積み残し

datasetは、🤗的にはdatasetsを使ってロードする方法に置き換えたいようです
LineByLineTextDatasetはdeprecatedっぽい雰囲気なので、datasetsでの読み込みの仕方を調べたい(以上、写経記事より)

『Transformerによる自然言語処理』3章では、カントの著作のテキストデータを使いました。
テキストファイルとして用意したデータをLineByLineTextDatasetクラスのインスタンスとします。

dataset = LineByLineTextDataset(
    tokenizer=tokenizer, file_path="kant.txt", block_size=128
)

これをdatasetsというライブラリを使って書き換えてみます。

なお、この書き換えはLineByLineTextDatasetインスタンス化したときにWarningで案内されています2

This dataset will be removed from the library soon, preprocessing should be handled with the 🤗 Datasets library.
You can have a look at this example script for pointers: https://github.com/huggingface/transformers/blob/main/examples/pytorch/language-modeling/run_mlm.py

参考例:examplesのlanguage-modeling/run_mlm.py

Warningで案内されたスクリプトを参考にします。

データの読み込みをたどると、大雑把に以下の流れと分かりました。

  • datasets.load_datasetで読み込み、raw_datasetsとする
  • raw_datasetstokenized_datasetsに変換(トークナイズ)
  • tokenized_datasetsは辞書のように扱える(DatasetDict
    • tokenized_datasets["train"]train_dataset
    • tokenized_datasets["validation"]eval_dataset

動作環境

  • Colab (Python 3.7系)
  • transformers 4.18.0
  • tokenizers 0.12.1
  • datasets 2.1.0
  • torch 1.11.0+cu113

datasetsライブラリで書き換え

ドキュメントを引きつつ、書き換えたのがこちら!

token_dir = Path("KantaiBERT")
tokenizer = RobertaTokenizer.from_pretrained(str(token_dir), max_length=512)
text_column_name = "text"


def tokenize_function(examples):
    examples[text_column_name] = [
        line
        for line in examples[text_column_name]
        # 空行や空白文字だけからなる行を除くことでline by lineにしている
        if len(line) > 0 and not line.isspace()
    ]
    return tokenizer(
        examples[text_column_name],
        padding=False,
        truncation=True,
        max_length=512,  # from_pretrainedのmax_lengthと揃えた
        return_special_tokens_mask=True,
    )


raw_datasets = load_dataset("text", data_files="kant.txt")
tokenized_datasets = raw_datasets.map(
    tokenize_function,
    batched=True,
    num_proc=None,
    remove_columns=[text_column_name],
    load_from_cache_file=True,
    desc="Running tokenizer on dataset line_by_line",
)
dataset = tokenized_datasets["train"]

書き換え解説

そもそもdatasetsライブラリにおけるdatasetは、以下を含むディレクトとのことです3

  • some data files in generic formats (JSON, CSV, Parquet, text, etc.)
  • and optionally a dataset script, if it requires some code to read the data files. This is used to load any kind of formats or structures.

今回はテキストファイルの読み込みなので、load_datasetの第1引数に"text"を指定して読み込みます。
ref: https://huggingface.co/docs/datasets/loading#text-files

3章の範囲では、trainとvalidationのsplitは考えていないので、load_datasetsplit引数は指定しません。
すると、load_datasetDatasetDictを返します4

返されたraw_datasetsDatasetDict)のキーを確認すると、trainだけを持ちます(繰り返しますが、trainとvalidationのsplitは考えていません)。
raw_datasets["train"]の各要素は辞書で、{"text": "kant.txtの1行"}という形式です(キーは自動でtextとなります)。
ここには空行も1要素として含んでいます。

DatasetDictmapメソッドで各データセットを変換できます。
tokenizerを使ってトークナイズする関数を定義し、mapメソッドに渡します。
この関数でLineByLineTextDatasetの場合と同じ要素数に揃います。

トークナイズする関数のシグネチャは、function(batch: Dict[List]) -> Union[Dict, Any]となります。5
batched引数にTrueを指定していて、with_indices引数はデフォルト値のFalseとなるためです。

そして、mapメソッドが返したtokenized_datasetsからtrainのデータセットを取り出しました(tokenized_datasets["train"]の要素の形式については後述します)。

今回validationは用意しませんでしたが、上記のコードに少し手を入れるだけでvalidationのトークナイズもできるでしょう!(early stoppingを試したいと思っています)

書き換えて3章

上記の書き換えを使い、『Transformerによる自然言語処理』3章の内容を実施したnotebookはこちらです。

書き換えたことの検証

LineByLineTextDatasetからdatasetsライブラリを使うように書き換え、デグレていないかを確認しました。

  • 書き換え前後でdatasetの長さが同じか
  • datasetの各要素について、トークナイズの結果(input_ids)が等しいか
from datasets import load_dataset
from transformers import LineByLineTextDataset, RobertaTokenizer

model_dir = "KantaiBERT"
text_column_name = "text"

tokenizer = RobertaTokenizer.from_pretrained(model_dir, max_length=512)


def tokenize_function(examples):
    examples[text_column_name] = [
        line
        for line in examples[text_column_name]
        if len(line) > 0 and not line.isspace()
    ]
    return tokenizer(
        examples[text_column_name],
        padding=False,
        truncation=True,
        max_length=512,
        return_special_tokens_mask=True,
    )


deprecated_dataset = LineByLineTextDataset(
    tokenizer=tokenizer, file_path="kant.txt", block_size=128
)

raw_datasets = load_dataset("text", data_files="kant.txt")
tokenized_datasets = raw_datasets.map(
    tokenize_function,
    batched=True,
    num_proc=None,
    remove_columns=[text_column_name],
    load_from_cache_file=True,
    desc="Running tokenizer on dataset line_by_line",
)
train_dataset = tokenized_datasets["train"]

assert len(deprecated_dataset) == len(train_dataset)

for old, new in zip(deprecated_dataset, train_dataset):
    assert old["input_ids"].tolist() == new["input_ids"]
  • LineByLineTextDatasetの要素は以下の辞書
    • キー: input_ids
    • input_idsの値はtorch.tensor
  • datasetsライブラリを使った実装では要素は以下の辞書
    • キー: input_ids, special_tokens_mask, attention_mask
    • input_idsの値はlist

スクリプトを実行したところ、AssertionErrorは送出されず、検証はパスしました。
デグレなしと言えると思います(検証項目の見落としに気づいた方はお知らせください)。
なお、書き換えた3章のコードを実行したところ、RoBERTaも事前訓練できていそうです✌️

終わりに

deprecatedと思われるLineByLineTextDatasetから、datasetsライブラリを使ったデータの読み込みに書き換えました!
主観でしかないですが、ちょっとだけモダンになった感じがします。

『Transformerによる自然言語処理』の訳者あとがきには、以下のようにあります。

(略)今後も技術発展とともに変更(※)が生じることが予想される。そのつもりで読んで対処していただきたい。(p.283)

(※)補足すると、コードやツールの変更

transformersを始めとするライブラリはたしかに変化が早いですね。
他の箇所でも新しい書き方ができることに気づいたら、またアウトプットしたいと思います。