Make組ブログ

Python、Webアプリや製品・サービス開発についてhirokikyが書きます。

1ヶ月に22本ブログ記事を書いて気づいたこと

正確にはまだ21.05本くらいで、今もその数値が少しずつ増えています。

さて AI校正Shodoアドベントカレンダーをやろうと思い立ったのは11月の中旬ごろでしたが、目的としてはもちろんマーケティング的な意味合いもありつつ、ドッグフーディング的な意義もありました。「大量にブログ記事を書いたときにどうなるのだろう?」と試すことです。

その結果、今さらながらブログを書くことについて色々と気づいたのでそれを残したいと思います。というよりも、今の時代だからこそ新しく気づけたことがありました。

気づいた:もっと小さい記事をたくさん書いても良い

なぜか「記事を書く」という感覚が重くなり、書くとなると妙に重厚な文章(もはやドキュメント)が完成する経験はないでしょうか。あれはもうやめちゃって、ショート動画感覚で読める軽い記事を書いたほうが、気持ちが楽なので良いと気づきました。

最近はQiitaの記事やZennの記事もしっかりと書かれたものが多く、書籍化するものも多くあります。昔の世の中は本当に雑なメモも多かったように思いますが、最近は「ちゃんと正しく網羅的でないと」という感覚があります。

ですがそうなるとどうしても書く気が起きにくい。それはもったいないわけですね。そこで軽めの記事を書いて、必要ならトピックごとに分割して連載するような形にすれば良いわけです。今回もOptuna Neuronに関する紹介と注意点(とあるバグ回避的な話)を分けましたが、自分の中で区切りもついて良かったと思います。

別にこれはマーケティングの数値的な意味があるというわけでなく、体感として気軽に書き始められるからというだけです。

気づいた:SNSでバズりたいと思わない

SNSでバズる必要も妙に意識する必要もない、そう思うと楽です。もちろん読み手への配慮や炎上しないよう気をつける必要はありますが、よしバズってやろうと書く必要はないということです。宣伝目的でキッチリ狙った記事を書いたり、時事ネタに突っ込むのが必要なときもありますが、基本的な備忘録や紹介などでわざわざ「いいね数」にとらわれる必要はないのかもしれません。

というのも今回、記事を書いていることや、特定の記事については読んでほしいような人からの反応が得られたからです。間違いも指摘してもらえましたし、自分なりに身近な人に伝えられて、自分も勉強になったので良かったなという話です。最近は絵にしろ文章にしろ、表現はすべて万人に受けてバズらなきゃいけない感覚がありますが、それはSNSを運営する人間の思惑なのかもしれません。

気づいた:リモートワークだから「書く脳」を鍛えておく

文章をたくさん書いていると、「書く脳」が動くのか書いていて苦じゃなくなります。先日、社内用のEsaに調べたことのまとめをしたのですが、公開できるレベルの記事を一発で書けた自分に驚きました(これが…力か…的な)。

リモートワークの是非について議論される昨今ですが、たしかにオンラインではコミュニケーションが難しいものです。そのときに単にSlackで会話したり、Zoomをつなぐのではなく、ドキュメントやタスクの説明としてちゃんとまとめるのが重要だと思います。そのときにこういった「書く脳」、書くことが苦ではない状態を持っておきたいものです。そうすれば自分も無理なくできますし、正確かつ無駄なく伝えられるはずです。

ブログを書く意味はあるのか?

ChatGPTに聞けば何でも回答を得られる時代ですが、だからこそ自分の言葉で書く意義が出てきます。たとえば伝えたいことであったり、自分で残しておきたいことだったり、ChatGPTの回答がイマイチだったりしたことは残す意味があると思います。要するに「情報として残す」というより「(未来の自分を含めて)伝えたいこととして残す」ということです。作品、とまではいかないですが。

ChatGPTなどのAIが成長してくると、人にとって重要なのは「その人である」ことだと思います。その人の触れたことを通しての作品であったり、その人の中にある世界観や生の人間としての何かです。僕はAIの会社をやっていますが、目的としては人間の創造・想像する力を引き出すことであり、それ以外の雑多なことをなくすことと決めています。最後は人の言葉や手によってしか人はケアされないのかもなと思っています。

だからこそ、もう少し気軽に記事を書いても良いんだなと思いました。

おわりに

くぅ~疲れましたw これにて完結です!

皆さま良いお年を。

qiita.com

執筆:@hirokiky
Shodoで執筆されました

requirements.txtでリポジトリーURLを指定する方法

requirements.txtでリポジトリーURLを指定したいことがあります。たとえばSageMakerではコード用のディレクトリーにrequirements.txtを配置することで、起動時に依存ライブラリーをインストールしてくれます。その際に問題になるのが、Neuron系のライブラリーではリポジトリーURLを指定する必要があることです。

$ pip install --extra-index-url https://pip.repos.neuron.amazonaws.com torch-neuronx torchvision

通常はこのようにコマンドラインからオプションを指定したり、 pip config set としてグローバルの設定を追加するものです。ですが設定できるポイントが requirements.txt しかない場合は以下のように対処できます。

requiremets.txtに--extra-index-urlオプションを書く

requirements.txtには --extra-index-url などのオプションを書けます。

--extra-index-url https://pip.repos.neuron.amazonaws.com
torch-neuronx==...
neuronx-cc==...
torchvision==...

これでAWS Neuron向けのリポジトリーを使ってインストールできます。

当該リポジトリーにないパッケージはどうなる?

オプションの指定が --extra-index-url なので、指定したリポジトリーにパッケージがない場合はPyPIからインストールされます。たとえばsentencepieceはAWS Neuronのリポジトリーにはありませんが、一緒に書いてもよしなにインストールしてくれます。

おわりに

もう明日はクリスマスですね。Shodoアドベントカレンダーもここまでこれて良かったです!

明日で最後!

qiita.com

執筆:@hirokiky
Shodoで執筆されました

Jupyter Notebookでマウスを使わない!

Jupyter Notebookでマウスを使わなければ気持ちよくなれます!という啓蒙活動です。
Jupyter NotebookというとGUIが基本なのでどうしてもマウスを動かしてしまいますが、キーボード操作がかなり充実しています。あまり知られていない感じもしますが、かなり快適に使えるのでぜひ活用していきましょう。

Jupyter Notebookの様子

入力中と、セル操作中の2つのモードがある

まず知るべきなのが、セルに対して2つのモードがあるということです。普段のようにセルの中身を書いているモードと、セルを操作したりするモードの2つがあります。感覚的には vi のように、入力モードとコマンドモードがあると思えば良いかもしれません。上記の画像では入力モードになっていますね。

Escキーを押下することで、入力モードからセルを操作するモードに移行できます。

セルの操作モード

このモードに入ることで、セルの操作を行えます。セル全体が選択されており、カーソルが表示されていないことに注目してください。

セルの操作をキーボードでやろう!

だいたい以下の操作を覚えておくと、キーボードだけでJupyter Notebookを使えます:

  • Enter:セルの入力モードに入る
  • Esc:セルの操作モードに戻る
  • b:下にセルを追加(Below)
  • a:上にセルを追加(Above)
  • or :セルを選択
  • Ctrl + Shift + or :セルを上下に移動
  • dd:セルの削除(Delete)
  • x:セルをカット
  • v:セルを下にペースト
  • c:セルをコピー
  • z:セル操作のUndo
  • y:コードセルに変更(なんで y なんでしょうね)
  • mMarkdownセルに変更(Markdown
  • 00カーネルのリスタート

これで一通りの操作をまかなえると思います。セルを間違えて消したときなども、冷静に z とするだけで戻るので便利です。

こんな感じでキーボードだけで操作しよう

Jupyter Notebookを使っていると前に戻ったり、セルのタイプを変えたりすることはよくあると思います。上記のキーボードによる操作を覚えておけば、そういった操作をマウスを触らずにサクサクと行えます!

キーボード操作の例

ちなみに今日、12月21日(1221)は回文数です!

他にも便利な操作がたくさん

Jupyter Notebookには他にも便利な操作がたくさんあるので、慣れてくればどんどん調べると良いでしょう。他におすすめなのが Shift + 上下でセルを選択して Shift + m でマージしたり、セル編集中に Ctrl + Shift + - でカーソル位置でセルを分割したりできます。あまり使う機会は多くないですが、覚えておくと便利です。

おわりに

ShodoのAIの開発時はJupyter Notebookが大活躍しています。操作の効率化のためには、マウスを触らないことが大事です。僕は昔、 keysnail というFirefox向けの神拡張を使っていて、すべてのブラウザ操作をキーボードで行っていました。Jupyter Notebookをキーボードだけで扱うと、当時のような心地よさを思い出せます。ぜひ皆さんもJupyter Notebookはマウスを触らずキーボードだけで使ってください!

現在、Shodoではアドベントカレンダー応援クーポンを配布しております。80%オフでShodoを最長3ヶ月間使えるクーポンです。以下のクーポンコードをご購入時に入力して、このアドベントカレンダーの季節にShodoのAI校正をブログの執筆にお役立てください。

XMAS2024

shodo.ink

執筆:@hirokiky
Shodoで執筆されました

Dockerで稼働しているCeleryが停止時にタスクを完了しない問題と修正方法

DockerでCeleryを稼働しているとき、設定によってはタスクを安全に完了(Graceful Shutdown)できていない場合があります。それぞれの登場人物の仕様を理解して間違いないようにしましょう。

コンテナランタイムの仕様

AWS Fargateではコンテナを停止する際、最初に SIGTERM が送信され、それでも30秒以内にプロセスが停止しない場合は SIGKILL が送信されます。この SIGTERM が適切にCeleryのプロセスに届いていないと安全なシャットダウンができません。

aws.amazon.com

CMDでシェル形式で書くとSIGTERMが届かない

Dockerでプロセスを起動する際、 CMD コマンドを指定することがあります。

CMD celery -A myapp

CMD をこのように指定する場合、Dockerは /bin/bash -c を先頭につけてこのコマンドを実行します。この場合 SIGTERMCMD に指定しているコマンドに届かず、 /bin/bash で止まってしまいます。

その結果、Dockerコンテナにコンテナランタイムが停止するよう命令しても、Celeryのようなタスクが完了せずに中断されてしまいます。

修正方法1:exec形式で書く

シェル形式でなくexec形式で書くと /bin/bash でラップされずに起動します。

CMD ["/usr/local/bin/celery", "-A", "myapp"]

Dockerfile リファレンス — Docker-docs-ja 24.0 ドキュメント

@naoina さんご指摘いただきありがとうございました!

修正方法2:ENTRYPOINTに書く

Celeryの起動コマンドをENTRYPOINTに設定し、CMDにはオプションパラメータを渡すように変更しても良いです。

ENTRYPOINT ["/usr/local/bin/celery"]
CMD ["-A", "myapp", "worker", "-l", "ERROR"]

こちらでもDockerコンテナが直接Celeryを起動するので、コンテナランタイムからのSIGTERMを受け取れます。

ENTRYPOINTとCMDとは何か

ENTRYPOINTには実行されるべきコマンドを書き、CMDには docker run で起動する際のデフォルトのオプションを書きます。デフォルトの実行コマンドを書き換えられたくない場合などにENTRYPOINTを使うことで、コンテナの用途を明確にできます。

www.docker.com

CMDだけの場合は2種類の書き方があるので、気をつけましょう。

とはいえSIGTERMの話とENTRYPOINT or CMDの話は直接的に関係がないので、よくDocker、bash、コンテナランタイム(Fargate)の挙動を知っておく必要があります。

おわりに

今回はとくにタスクの扱いが繊細なCeleryで書きましたが、他のアプリケーションでも同じことです。たとえばGunicornやFastAPIのBackgroundTaskの挙動も関係します。

現在、Shodoではアドベントカレンダー応援クーポンを配布しております。80%オフでShodoを最長3ヶ月間使えるクーポンです。以下のクーポンコードをご購入時に入力して、このアドベントカレンダーの季節にShodoのAI校正をお役立てください。

XMAS2024

shodo.ink

執筆:@hirokiky
Shodoで執筆されました

Optimum Neuronでresume_from_checkpointをするとTypeError None // int

先日の記事のように Optimun Neuronを使うとHuggingFace Trainerを簡単にAWS Neuron上で扱えます。

ですがまだあまり安定していないというのが正直なところです。今回の問題は0.0.23から0.0.25の範囲で確認済みです。記事に書いてしまっていますが、もし良ければちゃんと調査してIssueにしてください(すいません、時間が取れればやりたいと思ってます)。

Optimun Neuronでチェックポイントからの再開時にTypeError

Optimun Neuronでチェックポイントを残しつつ学習し、実際に resume_from_checkpoint=True をつけて再開すると以下の行で TypeError が発生します。

        train_bs_state = trainer_state.train_batch_size // max(1, training_args.n_gpu)

https://github.com/huggingface/transformers/blob/main/src/transformers/trainer.py#L1874

これは trainer_state.train_batch_size にNoneが設定されており、intとの比較でエラーとなっています。呼び出し元はこちら。

            self.compare_trainer_and_checkpoint_args(self.args, self.state)

https://github.com/huggingface/optimum-neuron/blob/v0.0.25/optimum/neuron/trainers.py#L900

Optimun Neuronの0.0.25ではNeuronTrainerの実装を見ても設定する処理がないようです。
https://github.com/huggingface/optimum-neuron/blob/v0.0.25/optimum/neuron/trainers.py

HuggingFace Trainerでは設定しています。
https://github.com/huggingface/transformers/blob/main/src/transformers/trainer.py#L2249

問題のとりあえずの回避方法

かなり雑な回避方法ですが、Optimun NeuronのNeuron Trainerでチェックポイントを今すぐ使いたい人は以下を参考にしてください(ちゃんと本家を修正しましょう。すいません)。この方法では読み込み時に、 training_state の値ではなく指定されたバッチサイズを使っています。

from optimum.neuron import NeuronTrainer

# Avoid a bug that NeuronTrainer does not store batch size to checkpoint
# And it cause None // int TypeError in compare_trainer_and_checkpoint_args.
class Trainer(NeuronTrainer):
    def compare_trainer_and_checkpoint_args(self, training_args, training_state):
        if training_state.train_batch_size is None:
            training_state.train_batch_size = self._train_batch_size
        super().compare_trainer_and_checkpoint_args(training_args, training_state)

バッチサイズの指定が別にならないようにだけは注意してください。

training_state.train_batch_size をちゃんと保存するようにしたかったのですが、良いところで処理を入れられずこう回避しています。

PRを出す人に向けて

個人的なメモでもありますが、PRを出す際はテストを修正して証明するのが手っ取り早いと思います。

ですが現状、Optimun Neuronで resume_from_checkpoint のテストはスキップされているようです。こちらを直してテストが通らないことを説明するのがPR提出のスタートラインになるかと思います。

https://github.com/huggingface/optimum-neuron/blob/v0.0.25/tests/test_trainers.py#L278

おわりに

Optimun Neuronはとても便利ですが、少しまだ不安定な様相も見せています。今後の開発に期待したいのと、同時に僕たちも協力していきたいものです。今回はこの記事に一旦まとめるということでご容赦ください(時間がとれればちゃんと検証してPRします)。

現在、Shodoではアドベントカレンダー応援クーポンを配布しております。80%オフでShodoを最長3ヶ月間使えるクーポンです。以下のクーポンコードをご購入時に入力して、このアドベントカレンダーの季節にShodoのAI校正をブログの執筆にお役立てください。

XMAS2024

shodo.ink

執筆:@hirokiky
Shodoで執筆されました

Optimun NeuronでHuggingFace TrainerをAWS Neuron上で簡単に動かす

Optimun Neuron を使うと、HuggingFace TrainerをAWS Neuron上で気軽に扱えるようになります。具体的には以下のように、TrainerとTrainingArgumentsを置き換えるだけです。

from optimum.neuron import NeuronTrainer as Trainer
from optimum.neuron import NeuronTrainingArguments as TrainingArguments

こうするとTrn1上で、今までと同じコードで学習が可能です。AWS Neuronを使う場合はモデルのコンパイルが必要なのですが、これも自動で行われます。学習の間に動的にコンパイルを実行し、学習をしてくれます。この動的なコンパイルは長くて30分ほどかかりますが、学習自体が速いため総合的な時間は短縮されるでしょう。

作成されるモデルはコンパイル済みのものではありません。AWS Neuronというとそれ専用になるイメージがありますが、大丈夫です。

スクリプトの実行時は torchrun というコマンドを使います。

torchrun --nproc_per_node=2 train_model.py

公式のサンプルノートブックがおすすめ

こちらに公式で公開されているノートブックが用意されています。やりたいことに合わせて参考にするのがおすすめです!

github.com

ただNotebookであれこれ試すとなるとNeuronのコンパイルが頻繁に走って面倒な場合があります。個人的には手元のGPUなどでしっかりチューニングしてから、AWS Neuronで学習させるのが良いと思います。データセットの準備なども手元で済ませるほうが良いです(これについては別途記事に)。

SageMakerでは勝手にtorchrunをしてくれる

SageMakerでTrn1を使う場合は、自動で train_model.pytorchrun で実行してくれます。ですのでやることといえばインポートを切り替えるくらいなのでとても便利です。

モデルのコンパイル時間にどれくらいかかるか?などもモデルによりけりなので、ぜひ一度試してみると良いでしょう。インスタンスタイプを変えるだけで、勝手にSageMakerがよしなにやってくれます。

他にも知っておくと良いかも、ということ

AWS Neuronでの学習について詳しく知りたいとき

Optimun Neuronを使うと良い感じにラップされすぎていて、逆に何をしているのか分かりにくいときがあります。
pytorh-xlaを使ってNeuron上でTrainerを使った学習のサンプルがAWSの公式であるのでこちらもおすすめです。

aws.amazon.com

この記事、以前のインタビューでもご一緒した常世さんが書いてらっしゃいます。

trn1.32xlargeでマルチノードを使う場合は設定が必要

Trn1でマルチノードで学習させる際は、EFAの設定が必要になります。SecurityGroupで所属するマシン同士ですべてのInbound、Outboundを許可したり、専用のネットワークインタフェースを作成する必要があります。EC2の場合、手動で準備する必要がありかなり手間は大きいです。以下のドキュメントでコマンドによる作成方法などが書かれていますのでこちらを参考にしてください。

awsdocs-neuron.readthedocs-hosted.com

設定がうまくいっているかチェックするコマンドがとても役立ちます。ネットワークのPing Pongも確認してくれるので、SecurityGroupの設定が正しいかなども確認できます(実際にこのコマンドのおかげで間違いに気づいたこともありました)。

チェックポイントから再開させるとバグ

Optimun Neuronのバージョンによってはチェックポイントから再開する際にバグが発生する可能性があります。細かいことは次回の記事で記載いたします。

おわりに

AWS NeuronはTrnもInfも情報が少ないですが、公式できちんと整備されたドキュメントやサンプルはたくさんあります。ぜひお手元のモデルでの学習に活用してみてください。ShodoではAWS Neuronも最大限活用し、AIを使ったサービス運営を行っています。

現在、Shodoではアドベントカレンダー応援クーポンを配布しております。80%オフでShodoを最長3ヶ月間使えるクーポンです。以下のクーポンコードをご購入時に入力して、このアドベントカレンダーの季節にShodoのAI校正をブログの執筆にお役立てください。

XMAS2024

shodo.ink

執筆:@hirokiky
Shodoで執筆されました

SageMakerでのトレーニング時は事前にデータをエンコードしておく

SageMakerでトレーニングする際、強めのマシンを使う場合は事前にデータをエンコードしておくと良いです。データ量が多いと dataset.map(encoder) をするのも案外時間がかかります。そうするとGPUやTrnを有効活用していない時間も課金されてしまうので、事前にエンコードしたものを使いましょう。とくにSpotInstanceを使って繰り返しSageMakerのサーバーを起動する場合はやっておくことをおすすめします。

事前にデータをエンコードする

事前にエンコードするには、以下のように普段通りのエンコードし、 dataset.save_to_disk() を呼び出します。

dataset = load_dataset(...)
encoder = Encoder()
dataset = dataset.map(encoder)
dataset = dataset.remove_columns([...])
dataset.save_to_disk("data/mydataset_enc/")

読み込む際は load_from_disk() を使います。

from datasets import load_from_disk

load_from_disk("data/mydataset_enc/")

このフォルダーをS3にアップロードしておきましょう。

SageMakerでエンコードしたデータセットを使う

SageMakerでの学習を開始する際に、先ほどアップロードしたS3のパスを指定します。

from sagemaker.huggingface import HuggingFace

estimator = HuggingFace(
    entry_point="train_model.py",
)
estimator.fit({
    "train": "s3://my-dataset/mydataset_enc/",
})

このように指定するとSageMakerが実行前に自動でこのフォルダーをダウンロードしてくれます。
SageMakerの内部で実行する train_model.py では以下のようにファイルを読み込みましょう。

dataset = load_from_disk(os.environ["SM_CHANNEL_TRAIN"])

以上です!

これでSageMakerを起動するたびに大規模なデータセットエンコードを待つ必要がありません。

おわりに

Shodoでは独自のAIモデルを使ってAI校正のサービスを提供しています。

現在、Shodoではアドベントカレンダー応援クーポンを配布しております。80%オフでShodoを最長3ヶ月間使えるクーポンです。以下のクーポンコードをご購入時に入力して、このアドベントカレンダーの季節にShodoのAI校正をブログの執筆にお役立てください。

XMAS2024

shodo.ink

執筆:@hirokiky
Shodoで執筆されました