<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ja">
	<title>loglog()</title>
	<link rel="self" type="application/atom+xml" href="https://yng87.page/blog/feed.xml"/>
  <link rel="alternate" type="text/html" href="https://yng87.page/blog/"/>
  
	<updated>2026-05-17T00:00:00+00:00</updated>
	
	<id>https://yng87.page/blog/feed.xml</id>
	<entry xml:lang="ja">
		<title>サーバーレス GPU の Modal でコールドスタートを計測する</title>
		<published>2026-05-17T00:00:00+00:00</published>
		<updated>2026-05-17T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2026/modal-coldstart/"/>
		<id>https://yng87.page/blog/2026/modal-coldstart/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2026/modal-coldstart/">&lt;h2 id=&quot;hazimeni&quot;&gt;はじめに&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;modal.com&#x2F;&quot;&gt;Modal&lt;&#x2F;a&gt; というサービスをご存知だろうか。Modal はサーバーレス GPU を提供している PaaS の一つである。有名どころでは &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;whyyoushouldjoin.substack.com&#x2F;p&#x2F;modal&quot;&gt;Substack が SageMaker から移行したという事例を載せており&lt;&#x2F;a&gt;、今非常に勢いがある企業の一つだ。サーバーレス GPU というのは要は AWS Lambda の GPU 版である。&lt;&#x2F;p&gt;
&lt;p&gt;サーバーレス GPU を使いたいのはどんな時だろうか？たとえばアイテムに対する埋め込み計算やタグづけなど、アイテムが新しく追加された時だけ実行すれば良いような計算で、かつその頻度がそこまで高くないようなものは、常時 GPU サーバーを確保しておくとアイドル時間ばかりでお金が無駄になる。そういう時はバッチやイベント駆動のワークフロー中でサーバーレス GPU 上の ML モデルを呼べると実際の推論分にしか課金されず便利だろう。&lt;&#x2F;p&gt;
&lt;p&gt;サーバーレスのサービスではしばしばコールドスタートにかかる時間が問題になる。GPU を使ったモデル推論ではこの問題がさらに顕著で、GPU 用の Docker イメージは大抵サイズが非常に大きいのでインスタンスを確保してイメージを pull して立ち上げるだけで時間がかかるし、さらにその上にモデルのウェイトをロードしなければならない。汎用的なサービスでやると分単位でかかるのが普通である。&lt;&#x2F;p&gt;
&lt;p&gt;Modal などのサーバーレス GPU 業者は独自の最適化技術でこのコールドスタートを非常に短く抑えていることを売りにしている。そこで今回はいくつかのシナリオでこのコールドスタートの時間を簡単に計測してみようと思う。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;modal-noshi-ifang&quot;&gt;Modal の使い方&lt;&#x2F;h2&gt;
&lt;p&gt;Modal には training や notebook インスタンスなどいくつかのサービスがあるがここでは inference について簡単に紹介する。以下は&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;modal.com&#x2F;products&#x2F;inference&quot;&gt;公式ページのトップに載っているコード例&lt;&#x2F;a&gt;である。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; modal&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;MODEL_NAME&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;black-forest-labs&#x2F;FLUX.1-schnell&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;image&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    modal&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Image&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;from_registry&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;nvidia&#x2F;cuda:12.4.0-devel-ubuntu22.04&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    .&lt;&#x2F;span&gt;&lt;span&gt;pip_install&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;torch&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;transformers&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;diffusers&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; ...&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;volume&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; modal&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Volume&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;from_name&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;flux-lora-models&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;@&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;app&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;cls&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;gpu&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;H100&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; image&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;image&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; volumes&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;loras&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; volume&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;class&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; FluxWithLoRA&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    @&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;modal&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;enter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; setup&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;pipeline&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; FluxPipeline&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;from_pretrained&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;MODEL_NAME&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;to&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;cuda&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;pipeline&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;load_and_fuse_lora&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    @&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;modal&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;method&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; generate_image&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; prompt&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language&quot;&gt; self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;pipeline&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;prompt&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;images&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;flux&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; FluxWithLoRA&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;flux&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;generate_image&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;remote&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;見ての通り、ベースの Docker イメージ・インストールする Python ライブラリ・GPU タイプのような設定を全て Python コードとして記述できる。&lt;code&gt;@app.cls&lt;&#x2F;code&gt; にそれらインフラ設定を書き、&lt;code&gt;@modal.enter()&lt;&#x2F;code&gt; でアプリケーションの初期化、&lt;code&gt;@modal.method()&lt;&#x2F;code&gt; で実際に呼び出す関数を実装する。デプロイもコマンドラインから &lt;code&gt;modal deploy app.py&lt;&#x2F;code&gt; を実行するだけで良い。GPU や CUDA が絡むイメージのビルドは落とし穴も多く、一回のビルドにも時間がかかるので個人的には苦痛を伴う作業だったが、Modal 側がある程度テンプレ化してくれているおかげでハマりづらい。&lt;&#x2F;p&gt;
&lt;p&gt;上記の例は SDK 経由のアクセスを想定しているが、後述のように一般的な Web エンドポイントも容易に作成可能である。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;setutoatupu&quot;&gt;セットアップ&lt;&#x2F;h2&gt;
&lt;p&gt;今回は以下の三つのセットアップで、&lt;code&gt;cl-nagoya&#x2F;ruri-v3-30m&lt;&#x2F;code&gt; と &lt;code&gt;cl-nagoya&#x2F;ruri-v3-310m&lt;&#x2F;code&gt; による文埋め込み計算をするエンドポイントを用意した。いずれも L4 GPU 上にデプロイし、&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;modal.com&#x2F;docs&#x2F;guide&#x2F;webhook-proxy-auth&quot;&gt;proxy auth&lt;&#x2F;a&gt; で認証を設定している。&lt;&#x2F;p&gt;

&lt;blockquote class=&quot;callout note has-title&quot;&gt;
  
  
  &lt;p class=&quot;title&quot;&gt;
    &lt;span class=&quot;icon&quot;&gt;
      &lt;svg xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;20&quot; height=&quot;20&quot;&gt;&lt;path d=&quot;M12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12C22 17.5228 17.5228 22 12 22ZM12 20C16.4183 20 20 16.4183 20 12C20 7.58172 16.4183 4 12 4C7.58172 4 4 7.58172 4 12C4 16.4183 7.58172 20 12 20ZM11 7H13V9H11V7ZM11 11H13V17H11V11Z&quot; fill=&quot;currentColor&quot;&gt;&lt;&#x2F;path&gt;&lt;&#x2F;svg&gt;
    &lt;&#x2F;span&gt;
    &lt;strong&gt;Note&lt;&#x2F;strong&gt;
  &lt;&#x2F;p&gt;
  &lt;div class=&quot;content&quot;&gt;
    &lt;p&gt;ビルドスクリプト・計測スクリプトは Claude Code と Codex に書いてもらった。&lt;&#x2F;p&gt;

  &lt;&#x2F;div&gt;
  
&lt;&#x2F;blockquote&gt;

&lt;h3 id=&quot;1-sentence-transformers-fastapi&quot;&gt;1. &lt;code&gt;sentence-transformers&lt;&#x2F;code&gt; + FastAPI&lt;&#x2F;h3&gt;
&lt;p&gt;FastAPI 内で &lt;code&gt;sentence-transformers&lt;&#x2F;code&gt; モデルによる埋め込み計算を行なってレスポンスするシンプルな方法。一応 &lt;code&gt;flash-attn&lt;&#x2F;code&gt; も入れている。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; modal&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;app&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; modal&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;App&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ruri-coldstart-st-cold&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model_volume&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; modal&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Volume&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;from_name&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ruri-weights&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; create_if_missing&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; _prefetch_30m&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    from&lt;&#x2F;span&gt;&lt;span&gt; huggingface_hub&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; snapshot_download&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    snapshot_download&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        repo_id&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;cl-nagoya&#x2F;ruri-v3-30m&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        local_dir&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;cache&#x2F;hf&#x2F;models&#x2F;ruri-v3-30m&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        cache_dir&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;cache&#x2F;hf&#x2F;hub&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    model_volume&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;commit&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;@&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;app&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;cls&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    image&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        modal&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Image&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;debian_slim&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;python_version&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;3.11&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span&gt;uv_pip_install&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;torch==2.6.0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;https:&#x2F;&#x2F;github.com&#x2F;Dao-AILab&#x2F;flash-attention&#x2F;releases&#x2F;download&#x2F;v2.7.4.post1&#x2F;flash_attn-2.7.4.post1+cu12torch2.6cxx11abiFALSE-cp311-cp311-linux_x86_64.whl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;sentence-transformers&amp;gt;=3.3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;transformers&amp;gt;=4.48&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;fastapi[standard]&amp;gt;=0.115&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span&gt;env&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;HF_HOME&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;cache&#x2F;hf&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;HF_HUB_CACHE&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;cache&#x2F;hf&#x2F;hub&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;HUGGINGFACE_HUB_CACHE&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;cache&#x2F;hf&#x2F;hub&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;TRANSFORMERS_CACHE&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;cache&#x2F;hf&#x2F;transformers&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;SENTENCE_TRANSFORMERS_HOME&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;cache&#x2F;hf&#x2F;sentence-transformers&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ENABLE_SNAPSHOT&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span&gt;run_function&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;_prefetch_30m&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; volumes&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;cache&#x2F;hf&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; model_volume&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    gpu&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;L4&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    scaledown_window&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;60&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    max_containers&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    volumes&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;cache&#x2F;hf&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; model_volume&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    enable_memory_snapshot&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;False&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;class&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; STRuri30m&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    @&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;modal&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;enter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; load&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        import&lt;&#x2F;span&gt;&lt;span&gt; os&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        os&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;environ&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;HF_HUB_OFFLINE&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        import&lt;&#x2F;span&gt;&lt;span&gt; torch&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        from&lt;&#x2F;span&gt;&lt;span&gt; sentence_transformers&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; SentenceTransformer&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;model&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; SentenceTransformer&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;cache&#x2F;hf&#x2F;models&#x2F;ruri-v3-30m&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;            device&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;cuda&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;            model_kwargs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;attn_implementation&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;flash_attention_2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;torch_dtype&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; torch&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;bfloat16&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;            local_files_only&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;            trust_remote_code&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;model&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;encode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ウォームアップ&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; normalize_embeddings&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    @&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;modal&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;asgi_app&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;requires_proxy_auth&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; web&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        from&lt;&#x2F;span&gt;&lt;span&gt; fastapi&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; FastAPI&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        api&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; FastAPI&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;        @&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;api&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;post&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;embed&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;        def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; embed&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;req&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; dict&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            inputs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; req&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;get&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;inputs&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            if&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; not&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; isinstance&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;inputs&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; list&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                inputs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;inputs&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            embs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language&quot;&gt; self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;model&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;encode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                inputs&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;                normalize_embeddings&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            )&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;tolist&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            return&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;embeddings&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; embs&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;dim&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;embs&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; if&lt;&#x2F;span&gt;&lt;span&gt; embs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; else&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        return&lt;&#x2F;span&gt;&lt;span&gt; api&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;イメージビルド時に、&lt;code&gt;_prefetch_30m&lt;&#x2F;code&gt; でついでにモデルを Modal の Volume に保存し、実際に使う際にネットワーク越しからモデルウェイトをダウンロードするのを回避しているのがポイント。FastAPI のエンドポイントは &lt;code&gt;@modal.asgi_app&lt;&#x2F;code&gt; で作成できる。&lt;&#x2F;p&gt;

&lt;blockquote class=&quot;callout tip has-title&quot;&gt;
  
  
  &lt;p class=&quot;title&quot;&gt;
    &lt;span class=&quot;icon&quot;&gt;
      &lt;svg xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;20&quot; height=&quot;20&quot;&gt;&lt;path d=&quot;M9.97308 18H11V13H13V18H14.0269C14.1589 16.7984 14.7721 15.8065 15.7676 14.7226C15.8797 14.6006 16.5988 13.8564 16.6841 13.7501C17.5318 12.6931 18 11.385 18 10C18 6.68629 15.3137 4 12 4C8.68629 4 6 6.68629 6 10C6 11.3843 6.46774 12.6917 7.31462 13.7484C7.40004 13.855 8.12081 14.6012 8.23154 14.7218C9.22766 15.8064 9.84103 16.7984 9.97308 18ZM10 20V21H14V20H10ZM5.75395 14.9992C4.65645 13.6297 4 11.8915 4 10C4 5.58172 7.58172 2 12 2C16.4183 2 20 5.58172 20 10C20 11.8925 19.3428 13.6315 18.2443 15.0014C17.624 15.7748 16 17 16 18.5V21C16 22.1046 15.1046 23 14 23H10C8.89543 23 8 22.1046 8 21V18.5C8 17 6.37458 15.7736 5.75395 14.9992Z&quot; fill=&quot;currentColor&quot;&gt;&lt;&#x2F;path&gt;&lt;&#x2F;svg&gt;
    &lt;&#x2F;span&gt;
    &lt;strong&gt;Tip&lt;&#x2F;strong&gt;
  &lt;&#x2F;p&gt;
  &lt;div class=&quot;content&quot;&gt;
    &lt;p&gt;ビルドや起動を高速化するために、&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;modal.com&#x2F;docs&#x2F;guide&#x2F;model-weights&quot;&gt;モデルウェイトは Modal Volume においてコンテナにマウントするのがベストプラクティス&lt;&#x2F;a&gt;である。&lt;&#x2F;p&gt;

  &lt;&#x2F;div&gt;
  
&lt;&#x2F;blockquote&gt;

&lt;h3 id=&quot;2-sentence-transformers-fastapi-memory-snapshots&quot;&gt;2. &lt;code&gt;sentence-transformers&lt;&#x2F;code&gt; + FastAPI + Memory Snapshots&lt;&#x2F;h3&gt;
&lt;p&gt;まだアルファ版だが、Modal には &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;modal.com&#x2F;docs&#x2F;guide&#x2F;memory-snapshots&quot;&gt;Memory Snapshots&lt;&#x2F;a&gt; という、コンテナの初期化処理をキャッシュしてくれるような機能がある。PyTorch の読み込みなどは結構時間がかかるので、これを使うと立ち上がりがより速くなるらしい。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;@&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;app&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;cls&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    image&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        modal&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Image&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;debian_slim&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;python_version&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;3.11&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;        ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span&gt;env&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ENABLE_SNAPSHOT&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;                ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span&gt;run_function&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;_prefetch_30m&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; volumes&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;cache&#x2F;hf&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; model_volume&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    enable_memory_snapshot&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    experimental_options&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;enable_gpu_snapshot&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; True&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;    ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;class&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; STRuri30m&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    @&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;modal&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;enter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;snap&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; load&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;        ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;3-text-embeddings-inference-tei&quot;&gt;3. Text Embeddings Inference (TEI)&lt;&#x2F;h3&gt;
&lt;p&gt;三つ目は FastAPI を立てるのではなく、Rust 製の高速な埋め込み推論サーバーである &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;huggingface&#x2F;text-embeddings-inference&quot;&gt;Text Embeddings Inference (TEI)&lt;&#x2F;a&gt; を利用する。このような場合には、バックグラウンドでサーバープロセスを起動し、Modal では &lt;code&gt;@modal.web_server&lt;&#x2F;code&gt; によってリクエストをそのままプロキシするのがスタンダードなようである。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#tei-model-location&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; socket&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; subprocess&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; time&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; modal&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;app&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; modal&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;App&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ruri-coldstart-tei&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model_volume&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; modal&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Volume&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;from_name&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ruri-weights&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; create_if_missing&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;PORT&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 8000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; spawn_server&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;model_id&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; subprocess&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Popen&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    proc&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; subprocess&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Popen&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        [&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;text-embeddings-router&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;--model-id&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            model_id&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;--huggingface-hub-cache&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;cache&#x2F;hf&#x2F;hub&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;--port&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;            str&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;PORT&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;--hostname&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;0.0.0.0&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        ]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    deadline&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;monotonic&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 300&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    while&lt;&#x2F;span&gt;&lt;span&gt; time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;monotonic&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span&gt; deadline&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        try&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            socket&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;create_connection&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;127.0.0.1&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; PORT&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; timeout&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;close&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            return&lt;&#x2F;span&gt;&lt;span&gt; proc&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        except&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;OSError&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; ConnectionRefusedError&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            if&lt;&#x2F;span&gt;&lt;span&gt; proc&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;poll&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; is&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; not&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; None&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;                raise&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; RuntimeError&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;                    f&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;text-embeddings-router exited with code &lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;{&lt;&#x2F;span&gt;&lt;span&gt;proc&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;returncode&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            time&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;sleep&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0.5&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    proc&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;terminate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    raise&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; RuntimeError&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;text-embeddings-router did not become ready within 300s&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; _prefetch_30m&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    spawn_server&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;cl-nagoya&#x2F;ruri-v3-30m&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;terminate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;@&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;app&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;cls&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    image&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        modal&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Image&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;from_registry&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;            &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ghcr.io&#x2F;huggingface&#x2F;text-embeddings-inference:89-1.9.3&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;            add_python&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;3.11&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span&gt;dockerfile_commands&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;ENTRYPOINT []&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span&gt;env&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;HF_HOME&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;cache&#x2F;hf&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;HF_HUB_CACHE&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;cache&#x2F;hf&#x2F;hub&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;HUGGINGFACE_HUB_CACHE&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;cache&#x2F;hf&#x2F;hub&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;TRANSFORMERS_CACHE&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;cache&#x2F;hf&#x2F;transformers&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;                &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;SENTENCE_TRANSFORMERS_HOME&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;cache&#x2F;hf&#x2F;sentence-transformers&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span&gt;run_function&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            _prefetch_30m&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;            gpu&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;L4&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;            volumes&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;cache&#x2F;hf&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; model_volume&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    gpu&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;L4&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    scaledown_window&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;60&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    max_containers&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    volumes&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;cache&#x2F;hf&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; model_volume&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;class&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; TEIRuri30m&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    @&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;modal&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;enter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; boot&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;proc&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; spawn_server&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;cl-nagoya&#x2F;ruri-v3-30m&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    @&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;modal&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;exit&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; shutdown&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;proc&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;terminate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;    @&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;modal&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt;web_server&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;port&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;8000&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; requires_proxy_auth&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; startup_timeout&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;300&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; serve&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        pass&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;ji-ce-jie-guo&quot;&gt;計測結果&lt;&#x2F;h2&gt;
&lt;p&gt;上記の三種類に大きさの異なるモデルを二種類使って計6パターンのエンドポイントを作成し、コールドスタートにかかる時間を計測してみた。なお Memory Snapshots を使う場合は事前にリクエストを送ってスナップショットが作られるようにしてから計測した。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#snapshot-detail&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;
以下はコンテナ0の状態からリクエストを投げて返ってくるまでの時間を何回か測ったうちの一つであり、各回で数秒のぶれくらいは普通にあると思って見て頂きたい。&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;パターン&lt;&#x2F;th&gt;&lt;th&gt;モデル&lt;&#x2F;th&gt;&lt;th&gt;コールドスタート(秒)&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;sentence-transformers&lt;&#x2F;td&gt;&lt;td&gt;ruri-v3-30m&lt;&#x2F;td&gt;&lt;td&gt;16.25&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;sentence-transformers&lt;&#x2F;td&gt;&lt;td&gt;ruri-v3-310m&lt;&#x2F;td&gt;&lt;td&gt;18.29&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;sentence-transformers (Memory Snapshotsあり)&lt;&#x2F;td&gt;&lt;td&gt;ruri-v3-30m&lt;&#x2F;td&gt;&lt;td&gt;5.27&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;sentence-transformers (Memory Snapshotsあり)&lt;&#x2F;td&gt;&lt;td&gt;ruri-v3-310m&lt;&#x2F;td&gt;&lt;td&gt;8.32&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;TEI&lt;&#x2F;td&gt;&lt;td&gt;ruri-v3-30m&lt;&#x2F;td&gt;&lt;td&gt;7.38&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;TEI&lt;&#x2F;td&gt;&lt;td&gt;ruri-v3-310m&lt;&#x2F;td&gt;&lt;td&gt;7.48&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;今回の計測では、TEI と Memory Snapshots ありの sentence-transformers が同程度に速かった。TEI は Python ライブラリ読み込みのオーバーヘッドが無いため高速に起動している。Memory Snapshots が使われた場合もオーバーヘッドがなくなるので同等レベルの速さが出ていた。モデルウェイトはコンテナにマウントしたボリュームから読み込んでいるので、今回使った小型のモデルであればサイズによる差は小さかった。また GPU で推論しているので、どの場合でも埋め込み計算自体は50-100ms 程度と高速に完了していた。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;saigoni&quot;&gt;さいごに&lt;&#x2F;h2&gt;
&lt;p&gt;GPU コンテナが数秒で起動するというのはすごい技術だなと思う。今回の検証に使ったコードは以下。



  



  



  


&lt;div class=&quot;github-card&quot;&gt;
  &lt;a class=&quot;github-card__link&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;yng87&amp;#x2F;modal-measure-coldstart&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;
    &lt;div class=&quot;github-card__badge&quot; aria-hidden=&quot;true&quot;&gt;
      &lt;svg xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;20&quot; height=&quot;20&quot;&gt;&lt;path d=&quot;M5.88401 18.6531C5.58404 18.4523 5.32587 18.1972 5.0239 17.8366C4.91473 17.7062 4.47283 17.1521 4.55811 17.258C4.09533 16.6831 3.80296 16.4168 3.50156 16.3087C2.9817 16.1223 2.7114 15.5497 2.89784 15.0298C3.08428 14.51 3.65685 14.2397 4.17672 14.4261C4.92936 14.696 5.43847 15.1609 6.12425 16.0141C6.03025 15.8972 6.46364 16.4408 6.55731 16.5526C6.74784 16.7802 6.88732 16.918 6.99629 16.9909C7.20118 17.128 7.58451 17.1871 8.14709 17.1308C8.17065 16.7487 8.24136 16.378 8.34919 16.0355C5.38097 15.3101 3.70116 13.3949 3.70116 9.63947C3.70116 8.4006 4.0704 7.28368 4.75917 6.34756C4.5415 5.45368 4.57433 4.37259 5.06092 3.15611C5.1725 2.87715 5.40361 2.66314 5.69031 2.57328C5.77242 2.54949 5.81791 2.5389 5.89878 2.52648C6.70167 2.40319 7.83573 2.69681 9.31449 3.62311C10.181 3.41855 11.0885 3.31476 12.0012 3.31476C12.9129 3.31476 13.8196 3.41835 14.6854 3.62253C16.1619 2.68976 17.2986 2.39625 18.1072 2.52627C18.1919 2.53988 18.2645 2.55758 18.3249 2.57741C18.6059 2.66967 18.8316 2.88155 18.9414 3.15611C19.4279 4.37232 19.4608 5.45319 19.2433 6.34695C19.9342 7.28313 20.3012 8.39184 20.3012 9.63947C20.3012 13.3966 18.627 15.3046 15.6588 16.0318C15.7837 16.4467 15.8496 16.9103 15.8496 17.4118C15.8496 18.0763 15.8471 18.7108 15.8424 19.4223C15.8412 19.6124 15.8397 19.8156 15.8375 20.1279C16.2129 20.2107 16.5229 20.5074 16.6031 20.9086C16.7114 21.4502 16.3602 21.977 15.8186 22.0853C14.6794 22.3132 13.8353 21.5535 13.8353 20.5608C13.8353 20.4705 13.836 20.3414 13.8375 20.1142C13.8398 19.8012 13.8412 19.5987 13.8425 19.4092C13.8471 18.7017 13.8496 18.0714 13.8496 17.4118C13.8496 16.7145 13.6664 16.26 13.4237 16.0508C12.7627 15.481 13.0977 14.3971 13.965 14.2996C16.9314 13.9663 18.3012 12.8174 18.3012 9.63947C18.3012 8.68484 17.9893 7.89547 17.3881 7.23534C17.1301 6.95209 17.0567 6.54634 17.199 6.19062C17.3647 5.77639 17.4354 5.2336 17.2941 4.57678L17.2847 4.57944C16.7928 4.71861 16.1744 5.01956 15.4261 5.52826C15.182 5.69413 14.8772 5.74377 14.5932 5.66388C13.7729 5.43319 12.8913 5.31476 12.0012 5.31476C11.111 5.31476 10.2294 5.43319 9.40916 5.66388C9.12662 5.74335 8.82344 5.69468 8.57997 5.53077C7.8274 5.02414 7.2056 4.72355 6.71079 4.58352C6.56735 5.23672 6.63814 5.77758 6.80336 6.19062C6.94565 6.54634 6.87219 6.95209 6.61423 7.23534C6.01715 7.89096 5.70116 8.69352 5.70116 9.63947C5.70116 12.8114 7.07225 13.9681 10.023 14.2996C10.8883 14.3969 11.2246 15.4767 10.5675 16.048C10.3751 16.2153 10.1384 16.7799 10.1384 17.4118V20.5608C10.1384 21.5472 9.30356 22.2866 8.17878 22.0898C7.63476 21.9946 7.27093 21.4764 7.36613 20.9324C7.43827 20.5201 7.75331 20.2114 8.13841 20.1274V19.1379C7.22829 19.1991 6.47656 19.0496 5.88401 18.6531Z&quot; fill=&quot;currentColor&quot;&gt;&lt;&#x2F;path&gt;&lt;&#x2F;svg&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;github-card__content&quot;&gt;
      &lt;div class=&quot;github-card__repo&quot;&gt;yng87&amp;#x2F;modal-measure-coldstart&lt;&#x2F;div&gt;
      
      &lt;div class=&quot;github-card__description&quot;&gt;Modal 上で ruri-v3 の cold start を TEI &amp;#x2F; Sentence Transformers &amp;#x2F; snapshot on-off で測る実験用リポジトリ。&lt;&#x2F;div&gt;
      
      
      &lt;div class=&quot;github-card__meta&quot;&gt;Python&lt;&#x2F;div&gt;
      
    &lt;&#x2F;div&gt;
  &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;tei-model-location&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;TEI の&lt;code&gt;--model-id&lt;&#x2F;code&gt;には好きな local path を設定できるため、Volume 上のモデルを適当な場所にマウントしてそのパスを指定すればリモートからのモデルダウンロードなしに起動できる。しかし &lt;code&gt;--model-id&lt;&#x2F;code&gt;に HF 上の model_id を指定した上で、&lt;code&gt;HUGGINGFACE_HUB_CACHE&lt;&#x2F;code&gt;にマウントした Volume からモデルをロードする方が明確に起動が速かった。何かしら最適化がされているのかもしれない。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;snapshot-detail&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;ただし、リクエストを送った先でスナップショットが利用可能になっているとは限らず（インスタンスタイプごとに必要だったりするようで、事前に作っても使われないことがある）、その場合はスナップショットの作成が走ってしまうので逆に遅くなってしまう。ここに載せた数値はスナップショットから復元が行われた場合の計測結果。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>『データ分析失敗事例集』を再読したが改めてデータサイエンスに関わる全ての人が読むべき本だと思った</title>
		<published>2026-01-24T00:00:00+00:00</published>
		<updated>2026-01-24T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2026/book-datascience-failures/"/>
		<id>https://yng87.page/blog/2026/book-datascience-failures/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2026/book-datascience-failures/">&lt;p&gt;様々なデータ分析現場での失敗事例をまとめた『データ分析失敗事例集』という本を数年ぶりに再読したのだが、生成 AI が流行してそれを使った PoC 施策がますます発生しがちな昨今、改めてデータサイエンスに関わる全員が読むべき価値のある本だと思ったので紹介したい。&lt;&#x2F;p&gt;

  



  



  



  



  



  



  



&lt;div class=&quot;embed-card embed-card--book&quot;&gt;
  &lt;a class=&quot;embed-card__link&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;www.kyoritsu-pub.co.jp&amp;#x2F;book&amp;#x2F;b10032587.html&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;
    &lt;div class=&quot;embed-card__media&quot;&gt;
      
      &lt;img class=&quot;embed-card__image no-lightense&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;hondana-image.s3.amazonaws.com&amp;#x2F;book&amp;#x2F;image&amp;#x2F;10032587&amp;#x2F;normal_c40c6796-08fb-4f83-a959-0bc3562f3635.jpg&quot; alt=&quot;データ分析失敗事例集&quot; loading=&quot;lazy&quot;&gt;
      
    &lt;&#x2F;div&gt;

    &lt;div class=&quot;embed-card__content&quot;&gt;
      
      &lt;div class=&quot;embed-card__label&quot;&gt;Book&lt;&#x2F;div&gt;
      

      &lt;div class=&quot;embed-card__title&quot;&gt;データ分析失敗事例集&lt;&#x2F;div&gt;

      
      &lt;div class=&quot;embed-card__meta&quot;&gt;尾花山和哉編著ほか &amp;#x2F; 共立出版&lt;&#x2F;div&gt;
      

      
      &lt;div class=&quot;embed-card__description&quot;&gt;失敗から学び、成功を手にする。&lt;&#x2F;div&gt;
      

      
      &lt;div class=&quot;embed-card__note&quot;&gt;2023&amp;#x2F;08&amp;#x2F;03発売 &amp;#x2F; ISBN 9784320125674&lt;&#x2F;div&gt;
      
    &lt;&#x2F;div&gt;
  &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;

&lt;p&gt;世の中で光が当たるのは大抵成功事例で、自分もニュースレターや技術ブログなどをフォローして、有名な企業の事例をよく読んでいる。ただ、その裏には表に出てこない失敗事例が多くあるのは現場で働いているみなさんならご存知の通りで、しかしそういう話はイベントの懇親会で近くの人と話すとかそういう機会でもないとなかなか入手できない。そのような影に葬られがちな話を収集し（匿名化して）扱っている点で、本書は非常に稀有である。&lt;&#x2F;p&gt;
&lt;p&gt;成功というのは運の要素が多くあり、またそのプロダクト特有の事情に左右されたりもするため、なかなか再現できないものである。一方で失敗は、結構限られたパターンに落ち着くことが多い。本書はそうした失敗事例を20個以上集めており、そのどれもが思い当たる節があったり、自分も同じ現場にいたら同じような判断をしてしまいそう、と感じるものになっている。&lt;&#x2F;p&gt;
&lt;p&gt;例えばロット単位でしか発注できない商品に対して個数単位の予測精度を向上させようと頑張ってしまう事例が紹介されている。自分も小売ではないが、予測結果がどう使われるかを意識しないモデリングをやってしまったことがある。&lt;&#x2F;p&gt;
&lt;p&gt;最近は AI の性能向上が凄まじく、とりあえず PoC してみよう、とりあえず作って動かしてみよう、というプロダクトアウト的な動きが強くなっているように感じる。個人的にはそれ自体は悪いことではないと思っているが、作って試していろいろやってみても、結局それがユーザーにとって価値がなければ、ビジネスとしては成功しない。&lt;&#x2F;p&gt;
&lt;p&gt;本書にはいろいろな失敗の形が出てくるが、そこから得られる教訓を一言で言うと「ビジネスとして意味のあることをやろう」という話だと理解している。&lt;&#x2F;p&gt;
&lt;p&gt;関係者全員がビジネスとして意味のある方向を向いていれば成功…とまでは行かずとも、想定外の分析結果が出てきた際にも軟着陸できる可能性がある。その一方で、誰かが技術的なことにしか興味を持たなかったり、自分の成果がどう使われるかを想像できなかったり、自分の発言を後から覆せなかったりすると失敗していくのだろう。また、自分の立場だけで物事を判断してしまうことも、失敗につながりやすい。&lt;&#x2F;p&gt;
&lt;p&gt;本書では、受託分析のように、複数の会社が関わるケースの失敗事例が多く出てくる。会社間でのコミュニケーションがどうしても取りづらかったり、納期がかっちり決まっていたり、クライアントサイドが IT に投資していないせいでデータが不完全だったりという構造的な要因が多いのだろうと察せられる。一方、自分の経験の範囲では、事業会社内の施策でも似たような失敗事例や失敗まで行かずとも、これって本当に意味のあることやってるのか？これだけリソースを費やして得られる利益が釣り合っているのか？と首を捻りたくなる事例は少なくない。&lt;&#x2F;p&gt;
&lt;p&gt;ここ数年で一気に高性能な学習済みモデルが使えるようになり、自然言語処理や画像処理は自分でモデルを作る方が今や少数派である。だからこそ、ML エンジニアやデータサイエンティストも、単に技術に詳しいだけではなく、それをどうビジネスとして意味のあるものにするかを考えなければならない時代になっている。&lt;&#x2F;p&gt;
&lt;p&gt;結局のところ、ビジネスとして役に立っているか・ユーザーの課題を解決できているか・会社の利益につながっているか、そういう観点を持ち続けることが重要で、この時代に改めて読むととても身につまされる本である。&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>ベースライン的なレコメンドモデルを作るのに RecTools が便利そう</title>
		<published>2025-08-31T00:00:00+00:00</published>
		<updated>2025-08-31T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2025/play-rectools/"/>
		<id>https://yng87.page/blog/2025/play-rectools/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2025/play-rectools/">&lt;h2 id=&quot;rekomendoyong-noturunituite&quot;&gt;レコメンド用のツールについて&lt;&#x2F;h2&gt;
&lt;p&gt;レコメンド界隈はみんなが使っている定番のライブラリというものがないように思う。例えば、自然言語処理では（色々と文句を言われることもあるが）Hugging Face が標準的に使われるようになっている。それに比べると、レコメンドはとりあえずこれ使っておけ、と言えるものが思い浮かばない。&lt;&#x2F;p&gt;
&lt;p&gt;ロジック&#x2F;モデル部分中心のものであれば &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;benfred&#x2F;implicit&quot;&gt;implicit&lt;&#x2F;a&gt; や &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;recbole.io&#x2F;&quot;&gt;RecBole&lt;&#x2F;a&gt; などポピュラーなライブラリはいくつかあるが、それに与えるデータの前処理とか結果のオフライン評価に関しては、それぞれの現場で固有のツールやライブラリが作られがちな気がしている。&lt;&#x2F;p&gt;
&lt;p&gt;大規模サービスを運営していて KPI を0.1%でも向上させることに意味があるようなテック企業を除けば、一般的な協調フィルタリングや評価指標を実装すればまずは十分だろうし、そうなると中核となるデータの処理方法も概ね共通してくる。&lt;&#x2F;p&gt;
&lt;p&gt;その割にはその辺まで含めたレコメンドシステムの構築を一通りカバーしているライブラリってないなと思っていたのだけど、最近知った RecTools はこの領域で有用なライブラリなのではないかと思ったので触ってみた。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#kikkake&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;



  



  



  


&lt;div class=&quot;github-card&quot;&gt;
  &lt;a class=&quot;github-card__link&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;MobileTeleSystems&amp;#x2F;RecTools&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;
    &lt;div class=&quot;github-card__badge&quot; aria-hidden=&quot;true&quot;&gt;
      &lt;svg xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;20&quot; height=&quot;20&quot;&gt;&lt;path d=&quot;M5.88401 18.6531C5.58404 18.4523 5.32587 18.1972 5.0239 17.8366C4.91473 17.7062 4.47283 17.1521 4.55811 17.258C4.09533 16.6831 3.80296 16.4168 3.50156 16.3087C2.9817 16.1223 2.7114 15.5497 2.89784 15.0298C3.08428 14.51 3.65685 14.2397 4.17672 14.4261C4.92936 14.696 5.43847 15.1609 6.12425 16.0141C6.03025 15.8972 6.46364 16.4408 6.55731 16.5526C6.74784 16.7802 6.88732 16.918 6.99629 16.9909C7.20118 17.128 7.58451 17.1871 8.14709 17.1308C8.17065 16.7487 8.24136 16.378 8.34919 16.0355C5.38097 15.3101 3.70116 13.3949 3.70116 9.63947C3.70116 8.4006 4.0704 7.28368 4.75917 6.34756C4.5415 5.45368 4.57433 4.37259 5.06092 3.15611C5.1725 2.87715 5.40361 2.66314 5.69031 2.57328C5.77242 2.54949 5.81791 2.5389 5.89878 2.52648C6.70167 2.40319 7.83573 2.69681 9.31449 3.62311C10.181 3.41855 11.0885 3.31476 12.0012 3.31476C12.9129 3.31476 13.8196 3.41835 14.6854 3.62253C16.1619 2.68976 17.2986 2.39625 18.1072 2.52627C18.1919 2.53988 18.2645 2.55758 18.3249 2.57741C18.6059 2.66967 18.8316 2.88155 18.9414 3.15611C19.4279 4.37232 19.4608 5.45319 19.2433 6.34695C19.9342 7.28313 20.3012 8.39184 20.3012 9.63947C20.3012 13.3966 18.627 15.3046 15.6588 16.0318C15.7837 16.4467 15.8496 16.9103 15.8496 17.4118C15.8496 18.0763 15.8471 18.7108 15.8424 19.4223C15.8412 19.6124 15.8397 19.8156 15.8375 20.1279C16.2129 20.2107 16.5229 20.5074 16.6031 20.9086C16.7114 21.4502 16.3602 21.977 15.8186 22.0853C14.6794 22.3132 13.8353 21.5535 13.8353 20.5608C13.8353 20.4705 13.836 20.3414 13.8375 20.1142C13.8398 19.8012 13.8412 19.5987 13.8425 19.4092C13.8471 18.7017 13.8496 18.0714 13.8496 17.4118C13.8496 16.7145 13.6664 16.26 13.4237 16.0508C12.7627 15.481 13.0977 14.3971 13.965 14.2996C16.9314 13.9663 18.3012 12.8174 18.3012 9.63947C18.3012 8.68484 17.9893 7.89547 17.3881 7.23534C17.1301 6.95209 17.0567 6.54634 17.199 6.19062C17.3647 5.77639 17.4354 5.2336 17.2941 4.57678L17.2847 4.57944C16.7928 4.71861 16.1744 5.01956 15.4261 5.52826C15.182 5.69413 14.8772 5.74377 14.5932 5.66388C13.7729 5.43319 12.8913 5.31476 12.0012 5.31476C11.111 5.31476 10.2294 5.43319 9.40916 5.66388C9.12662 5.74335 8.82344 5.69468 8.57997 5.53077C7.8274 5.02414 7.2056 4.72355 6.71079 4.58352C6.56735 5.23672 6.63814 5.77758 6.80336 6.19062C6.94565 6.54634 6.87219 6.95209 6.61423 7.23534C6.01715 7.89096 5.70116 8.69352 5.70116 9.63947C5.70116 12.8114 7.07225 13.9681 10.023 14.2996C10.8883 14.3969 11.2246 15.4767 10.5675 16.048C10.3751 16.2153 10.1384 16.7799 10.1384 17.4118V20.5608C10.1384 21.5472 9.30356 22.2866 8.17878 22.0898C7.63476 21.9946 7.27093 21.4764 7.36613 20.9324C7.43827 20.5201 7.75331 20.2114 8.13841 20.1274V19.1379C7.22829 19.1991 6.47656 19.0496 5.88401 18.6531Z&quot; fill=&quot;currentColor&quot;&gt;&lt;&#x2F;path&gt;&lt;&#x2F;svg&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;github-card__content&quot;&gt;
      &lt;div class=&quot;github-card__repo&quot;&gt;MobileTeleSystems&amp;#x2F;RecTools&lt;&#x2F;div&gt;
      
      &lt;div class=&quot;github-card__description&quot;&gt;RecTools - library to build Recommendation Systems easier and faster than ever before&lt;&#x2F;div&gt;
      
      
      &lt;div class=&quot;github-card__meta&quot;&gt;Python&lt;&#x2F;div&gt;
      
    &lt;&#x2F;div&gt;
  &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;h2 id=&quot;rectools-nituite&quot;&gt;RecTools について&lt;&#x2F;h2&gt;
&lt;p&gt;RecTools はロシアでクラウドサービスを提供している MWS という会社が開発している OSS である。ドキュメントには&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;RecTools is an easy-to-use Python library which makes the process of building recommendation systems easier, faster and more structured than ever before. The aim is to collect ready-to-use solutions and best practices in one place to make processes of creating your first MVP and deploying model to production as fast and easy as possible.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;と書かれており、レコメンドシステムを構築するプロセス全体をカバーするようなライブラリとして作成されているのがわかる。&lt;&#x2F;p&gt;
&lt;p&gt;RecTools での学習・推論・評価パイプラインは以下のようになる。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; pandas&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; as&lt;&#x2F;span&gt;&lt;span&gt; pd&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; rectools&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; Columns&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; rectools&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;dataset&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; Dataset&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; rectools&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;models&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; SASRecModel&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; rectools&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;metrics&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; NDCG&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; Recall&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; Serendipity&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; calc_metrics&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; rectools&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;models&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; load_model&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; Interaction データの用意&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; カラムが (user_id, item_id, weight, datetime) となる pandas dataframe を渡す&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;dataset&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; Dataset&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;construct&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;interaction&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; 学習&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; SASRecModel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;fit&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;dataset&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;save&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;model.pkl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; 推論&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; load_model&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;model.pkl&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;recommendations&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; model&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;recommend&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    users&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;interaction&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;Columns&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;User&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;unique&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    dataset&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;dataset&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    k&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    filter_viewed&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; 評価&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;metrics&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;NDCG@10&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; NDCG&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;k&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; divide_by_achievable&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Recall@10&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Recall&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;k&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;    &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Serendipity@10&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Serendipity&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;k&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;metric_values&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; calc_metrics&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    metrics&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    reco&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;recommendations&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    interactions&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;interactions_test&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; 評価データ (pandas dataframe)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    catalog&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;interaction&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;Columns&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Item&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;unique&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;学習には (user, item) の interaction データセットを pandas データフレームとして用意する。一部のモデルではさらに追加で特徴量を与えることもできる。&lt;&#x2F;p&gt;
&lt;p&gt;推薦モデルを作るときは生のユーザー・アイテム ID（外部 ID）を、0から始まる整数の ID（内部 ID）に変換する処理をよく実行するが、そのようなよくある前処理は全て &lt;code&gt;Dataset&lt;&#x2F;code&gt; クラスが受け持ってくれるため自分で実装する必要がない。&lt;&#x2F;p&gt;
&lt;p&gt;また使えるモデルも ItemkNN など基本のものから、implicit や LightFM などの定番ライブラリのラッパー、SASRec や BERT4Rec などのニューラル系列推薦モデルまで基本的なものは一通り揃っており、またそれらが共通のインターフェースで使える。BPR など単純な (user, item) interaction を学習するモデルにも、系列推薦をするモデルにも同じ形式のデータセットを与えれば学習・推論・評価ができるのは便利である。&lt;&#x2F;p&gt;
&lt;p&gt;評価指標についても基本の Recall@K や NDCG@K から、serendipity などの beyond accuracy 指標、さらにバイアス除去系のオプションまで揃っているので困ることはなさそうである。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rectools-deshi-wu-deyaruyounaxue-xi-tui-lun-paipuraingazu-meruka&quot;&gt;RecTools で実務でやるような学習・推論パイプラインが組めるか？&lt;&#x2F;h2&gt;
&lt;p&gt;概要は上に紹介したので、ここからは実際に実務でモデルを作るのに必要な機能があるか調べていこうと思う。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;detafen-ge&quot;&gt;データ分割&lt;&#x2F;h3&gt;
&lt;p&gt;実務でのレコメンドでは、系列推薦モデルを使っていなくても、train&#x2F;test のデータ分割を時系列で行うことは多い。サービスで使う以上モデルに持たせたい能力は未来予測だからだ。RecTools では interaction データの最後の N 個を評価用に分割するための &lt;code&gt;LastNSplitter&lt;&#x2F;code&gt;、決まった時間幅のデータを評価用に分割するための &lt;code&gt;TimeRangeSplitter&lt;&#x2F;code&gt; が用意されており、時系列に沿った分割が簡単にできる。&lt;&#x2F;p&gt;
&lt;p&gt;以下はユーザーごとに行動を時系列で並べ、最後の一個を評価用に、それ以前を学習用に分割する例である&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; rectools&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;dataset&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; Dataset&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; rectools&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;model_selection&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; LastNSplitter&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ds&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; Dataset&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;construct&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;df&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; pandas -&amp;gt; `Dataset`&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;splitter&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; LastNSplitter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    n&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; leave-last-one-out&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    n_splits&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;  #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; train&#x2F;test&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    filter_cold_items&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;False&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    filter_cold_users&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;False&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    filter_already_seen&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;iterator&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; splitter&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;split&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;ds&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;interactions&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;train_idx&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; test_idx&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; _&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; next&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;iterator&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ds_train&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; ds&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;filter_interactions&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;train_idx&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ds_test&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; ds&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;filter_interactions&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;test_idx&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;n_splits&lt;&#x2F;code&gt; を増やすことで cross validation 用に複数 fold を生成できる。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;kasutamu-trainer-noli-yong&quot;&gt;カスタム Trainer の利用&lt;&#x2F;h3&gt;
&lt;p&gt;ニューラルネットワーク系のモデルは内部的には pytorch と pytorch lightning で実装されている。何も指定しなくても内部でデフォルトの Trainer インスタンスを生成してくれるが、学習の制御のためカスタマイズしたいことは多い。そのような場合は、モデルのコンストラクタに Trainer を返す関数を与える。&lt;&#x2F;p&gt;
&lt;p&gt;SASRec モデルを例とすると以下のように書ける&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; rectools&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;models&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; SASRecModel&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; pytorch_lightning&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; Trainer&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; get_trainer&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;checkpoint_dir&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Trainer&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;    ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; SASRecModel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    get_trainer_func&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;get_trainer&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    get_trainer_func_kwargs&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;checkpoint_dir&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; checkpoint_dir&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;get_trainer&lt;&#x2F;code&gt; の引数は別途 &lt;code&gt;get_trainer_func_kwargs&lt;&#x2F;code&gt; で渡す必要がある。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;early-stopping-yatietukupointo&quot;&gt;Early stopping やチェックポイント&lt;&#x2F;h3&gt;
&lt;p&gt;これもニューラルネットワーク系のモデルの場合だが、学習時に validation セットにおけるメトリクスを監視して early stopping をしたり、途中経過をチェックポイントとして残したいことは多い。&lt;&#x2F;p&gt;
&lt;p&gt;その場合、ドキュメントの &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;rectools.readthedocs.io&#x2F;en&#x2F;stable&#x2F;examples&#x2F;tutorials&#x2F;transformers_advanced_training_guide.html#&quot;&gt;Transformer Models Advanced Training Guide&lt;&#x2F;a&gt; にあるように、まずは毎エポック validation メトリクスを計算する callback を実装する必要がある。&lt;&#x2F;p&gt;
&lt;details &gt;
  &lt;summary&gt;&lt;span&gt;Validation メトリクスを計算する callback&lt;&#x2F;span&gt;&lt;&#x2F;summary&gt;
  &lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; pytorch_lightning&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;callbacks&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; Callback&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;class&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; ValidationMetrics&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt;Callback&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    def&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; __init__&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; top_k&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; val_metrics&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; dict&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; verbose&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; None&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;top_k&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; top_k&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;val_metrics&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; val_metrics&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;verbose&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; verbose&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;epoch_n_users&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;batch_metrics&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; list&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;dict&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; float&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; on_validation_batch_end&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;        trainer&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Trainer&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;        pl_module&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; LightningModule&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;        outputs&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; dict&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; torch&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Tensor&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;        batch&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; dict&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;str&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; torch&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Tensor&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;        batch_idx&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;        dataloader_idx&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; int&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;span&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; None&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        logits&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; outputs&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;logits&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        if&lt;&#x2F;span&gt;&lt;span&gt; logits&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; is&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; None&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            logits&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; pl_module&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;torch_model&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;encode_sessions&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;batch&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; pl_module&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;item_embs&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                :&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; -&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; :&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            ]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        _&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; sorted_batch_recos&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; logits&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;topk&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;k&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;top_k&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        batch_recos&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; sorted_batch_recos&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;tolist&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        targets&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; batch&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;y&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;tolist&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        batch_val_users&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; list&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            itertools&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;chain&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;from_iterable&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                itertools&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;repeat&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;idx&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;recos&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;                for&lt;&#x2F;span&gt;&lt;span&gt; idx&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; recos&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; enumerate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;batch_recos&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        batch_target_users&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; list&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            itertools&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;chain&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;from_iterable&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                itertools&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;repeat&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;idx&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;targets&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;                for&lt;&#x2F;span&gt;&lt;span&gt; idx&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; targets&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; enumerate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;targets&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        batch_recos_df&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; pd&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;DataFrame&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                Columns&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;User&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; batch_val_users&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                Columns&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Item&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; list&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;itertools&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;chain&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;from_iterable&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;batch_recos&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        batch_recos_df&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;Columns&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Rank&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            batch_recos_df&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;groupby&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Columns&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;User&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; sort&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;False&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;cumcount&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; +&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        interactions&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; pd&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;DataFrame&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                Columns&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;User&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; batch_target_users&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                Columns&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Item&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; list&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;itertools&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;chain&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;from_iterable&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;targets&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        prev_interactions&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; pl_module&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;data_preparator&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;train_dataset&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;interactions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;df&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        catalog&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; prev_interactions&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;Columns&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Item&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;unique&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        batch_metrics&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; calc_metrics&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;            self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;val_metrics&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; batch_recos_df&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; interactions&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; prev_interactions&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; catalog&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        batch_n_users&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; batch&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;shape&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;batch_metrics&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;append&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            {&lt;&#x2F;span&gt;&lt;span&gt;metric&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; value&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; batch_n_users&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; for&lt;&#x2F;span&gt;&lt;span&gt; metric&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; value&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; batch_metrics&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;items&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;epoch_n_users&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; +=&lt;&#x2F;span&gt;&lt;span&gt; batch_n_users&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;    def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; on_validation_epoch_end&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; trainer&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Trainer&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; pl_module&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; LightningModule&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;span&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; None&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        epoch_metrics&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; dict&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;map&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Counter&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language&quot;&gt; self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;batch_metrics&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; Counter&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        epoch_metrics&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            metric&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; value&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language&quot;&gt; self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;epoch_n_users&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;            for&lt;&#x2F;span&gt;&lt;span&gt; metric&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; value&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; epoch_metrics&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;items&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        }&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;log_dict&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            epoch_metrics&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; on_step&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;False&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; on_epoch&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; prog_bar&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;verbose&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;batch_metrics&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;clear&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-language&quot;&gt;        self&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;epoch_n_users&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;&#x2F;details&gt;
&lt;p&gt;そしてこの callback を上の方法で Trainer に設定する&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; pytorch_lightning&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;callbacks&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; EarlyStopping&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ModelCheckpoint&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; rectools&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;metrics&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; NDCG&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; pytorch_lightning&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; Trainer&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; get_trainer&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;checkpoint_dir&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; str&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Trainer&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    metrics&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;val_NDCG@10&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; NDCG&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;k&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; divide_by_achievable&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    val_metrics_callback&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; ValidationMetrics&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        top_k&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;10&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; val_metrics&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;metrics&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; verbose&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    best_ndcg_ckpt&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; ModelCheckpoint&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        dirpath&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;checkpoint_dir&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        monitor&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;val_NDCG@10&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        mode&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;max&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        filename&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;{epoch}&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;{val_NDCG@10:.4f}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    early_stopping_callback&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; EarlyStopping&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        monitor&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;val_NDCG@10&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        mode&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;max&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; Trainer&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        enable_checkpointing&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;        callbacks&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;val_metrics_callback&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; early_stopping_callback&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; best_ndcg_ckpt&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;        ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;また validation 用のデータを作るため学習データをさらに分割する必要があるが、これは &lt;code&gt;LastNSplitter&lt;&#x2F;code&gt; などを使わず、学習データのうちどこが validation かを表すマスクを渡すことが想定されているようである。以下は学習データのなかで各ユーザーごとに最後のアイテムを validation として使う例。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; rectools&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; Columns&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; get_val_mask_func&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;interactions&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; pd&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;DataFrame&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt; -&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; np&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;ndarray&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    rank&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        interactions&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;sort_values&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Columns&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Datetime&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; ascending&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;False&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; kind&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;stable&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span&gt;groupby&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;Columns&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;User&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; sort&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;False&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        .&lt;&#x2F;span&gt;&lt;span&gt;cumcount&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    val_mask&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; rank&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; ==&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; val_mask&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;values&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; SASRecModel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    get_val_mask_func&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;get_val_mask_func&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;    ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;以上の準備をして &lt;code&gt;model.fit(ds_train)&lt;&#x2F;code&gt; をすれば、指定したメトリクスに基づいた early stopping が実行され、その過程のチェックポイントを残すことができる。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rectools-gaxiang-iteinaikoto&quot;&gt;RecTools が向いていないこと&lt;&#x2F;h2&gt;
&lt;p&gt;以上見てきたようにベースライン的なモデルであればそれなりの柔軟性を持ちつつ簡単に学習から評価まで回せそうである。一つ難点を挙げるとすれば、基本的にデータセットを pandas のデータフレームとして扱うため、一度にメモリに乗り切らないような大規模データを扱うことは向いていない。あくまで小〜中規模のデータセットに対してサクッとベースライン性能を持つレコメンドシステムを作るためのツールだと思うのが良さそうである。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;kikkake&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;Bluesky で &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;bsky.app&#x2F;profile&#x2F;chezo.uno&quot;&gt;Aki Ariga さん&lt;&#x2F;a&gt;がコントリビュートしているのを見たことで知った。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>Claude Code で RSS&#x2F;Atom フィードリーダーを作ってみた感想</title>
		<published>2025-07-05T00:00:00+00:00</published>
		<updated>2025-07-05T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2025/claude-code-rss-reader/"/>
		<id>https://yng87.page/blog/2025/claude-code-rss-reader/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2025/claude-code-rss-reader/">&lt;p&gt;最近世の中の人々が熱狂している Claude Code で自分も遊んでみようと思い、とりあえずネタとして RSS&#x2F;Atom のフィードリーダーを作らせてみたので感想を書く。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zuo-tutamono&quot;&gt;作ったもの&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;blog&#x2F;2025&#x2F;feedwise.png&quot; alt=&quot;app screenshot&quot; &#x2F;&gt;
&lt;img src=&quot;&#x2F;blog&#x2F;2025&#x2F;semantic_search.png&quot; alt=&quot;semantic search&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;RSS&#x2F;Atom フィードの購読をするための Web アプリを作った
&lt;ul&gt;
&lt;li&gt;自分は普段 RSS で技術的な情報収集や趣味の合う人のフォローをしているが、既存のツールではなく自分用にチューニングされたものを作ってみたかった&lt;&#x2F;li&gt;
&lt;li&gt;自分でコードはほぼ書かず Claude Code に指示をするだけという純正の vibe coding をした&lt;&#x2F;li&gt;
&lt;li&gt;Pro プランで利用したため、2,3時間使うと制限がかかり元に戻るのに数時間かかった&lt;&#x2F;li&gt;
&lt;li&gt;休日や平日の夜に少しずつ作り、一週間くらいかけて概ねクラウドの VM 上でまともに動くものができた&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;作った機能は
&lt;ul&gt;
&lt;li&gt;フィードをフォローし、新着記事の取得と既読管理を行う機能&lt;&#x2F;li&gt;
&lt;li&gt;AI による要約を自動生成する機能&lt;&#x2F;li&gt;
&lt;li&gt;記事をベクトル化し semantic search できる機能。また検索結果の AI による要約機能
&lt;ul&gt;
&lt;li&gt;これはまだ全然作りこめていなくてあまり良い結果が返ってこない&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;ji-shu-de-nazhong-shen&quot;&gt;技術的な中身&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;Next.js でフロントエンドを、FastAPI でバックエンドを作成&lt;&#x2F;li&gt;
&lt;li&gt;DB には PostgreSQL を、バックグラウンドタスクには Celery を使い、メッセージ broker には redis を採用&lt;&#x2F;li&gt;
&lt;li&gt;PostgreSQL は &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;pgvector&#x2F;pgvector&quot;&gt;pgvector&lt;&#x2F;a&gt; を使いベクトル検索ができるようにしている&lt;&#x2F;li&gt;
&lt;li&gt;個人用の小規模なアプリケーションでスケーラビリティとか高可用性とかそういうものは要らないので、全て docker compose で起動して、一つの VM 上で動かしている&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;zuo-tutemitagan-xiang&quot;&gt;作ってみた感想&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;claude-code-nosugoitokoro&quot;&gt;Claude Code のすごいところ&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;概ね期待した通りに動作するアプリケーションが爆速でできる。色々とすごいところはあるがここに尽きる&lt;&#x2F;li&gt;
&lt;li&gt;RDB 周りの設計など、古典的な技術は強く、一発でそれなりのものが出てくる&lt;&#x2F;li&gt;
&lt;li&gt;フロントエンドも曖昧な指示をちゃんと良い感じの UI に変換してくれる
&lt;ul&gt;
&lt;li&gt;フロントエンドの知識が皆無なので、今まで一人でこの手のアプリケーションを作ることができなかったが、Calude Code によってハードルがぐっと下がった&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;Claude Code が自分で &lt;code&gt;docker compose up -d&lt;&#x2F;code&gt; をしてログを見て自力で修正するというのが結構上手くまわる&lt;&#x2F;li&gt;
&lt;li&gt;作り直しが容易
&lt;ul&gt;
&lt;li&gt;最初はあまり深いことを考えずローカルで sqlite で DB を作って、ベクトル検索は適当に duckdb を使い、ある程度固まったら Cloud Run で動かしていた。しかし、DB のファイルを GCS に置いて fuse マウントしていたため write で不整合が出たりして使い物にならなかったのでやめた&lt;&#x2F;li&gt;
&lt;li&gt;そのタイミングで一から設計を練り直すという指示を出し、とりあえず VM 一個で作れる安い構成ということで上記を採用した&lt;&#x2F;li&gt;
&lt;li&gt;自分でコードを書いていないから捨てやすいし、新しい構成で書く時もなんとなく動くまでが早いので助かる&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;claude-code-nowei-miao-natokoro&quot;&gt;Claude Code の微妙なところ&lt;&#x2F;h3&gt;
&lt;ul&gt;
&lt;li&gt;基本的にコード品質が低い
&lt;ul&gt;
&lt;li&gt;設計が雑で、DB や外部 API に接続するクライアントをいたるところで生で触っている。Repository クラスなどに包んで抽象化してくれと頼むとその場ではやってくれるがすぐに忘れてしまう。
&lt;ul&gt;
&lt;li&gt;まあ AI しか読まないならこれでも良いのかもしれない&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;FastAPI で同期と非同期が無秩序に混在する API を書いていた&lt;&#x2F;li&gt;
&lt;li&gt;ロジックの内部の深いところですぐに例外を握りつぶしてくる&lt;&#x2F;li&gt;
&lt;li&gt;ログメッセージが過度に装飾的（✅のような絵文字が大量に入ってくる）&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;クラウド環境にデプロイするのは詰まりがちだった
&lt;ul&gt;
&lt;li&gt;IAM の権限に原因があるのに一向に辿り着けていなかったり&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;フロントエンドのバグ修正はブラウザの操作が必要なので結構難航する
&lt;ul&gt;
&lt;li&gt;これは &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;simonwillison.net&#x2F;2025&#x2F;Jul&#x2F;1&#x2F;using-playwright-mcp-with-claude-code&#x2F;&quot;&gt;TIL: Using Playwright MCP with Claude Code&lt;&#x2F;a&gt; のように、MCP を上手く使えば解決する問題かもしれない&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;matome&quot;&gt;まとめ&lt;&#x2F;h2&gt;
&lt;p&gt;Claude Code は趣味で何か便利なものを作ったり、業務でもチーム内だけで触る程度のデモアプリを作るのにはかなり役立ちそう。普段仕事ではバックエンドしか触らず知識もそこに偏っているので、ちゃんとブラウザで動くものを自分一人で作れるようになるというのは体験として印象的。&lt;&#x2F;p&gt;
&lt;p&gt;技術の進歩で、楽器を買ってメンバーを集めてスタジオで録音して CD に焼いたりすることなく音楽が発表できるようになったのと同じように、個人開発者の創造性を高めるツールだと思う。&lt;&#x2F;p&gt;
&lt;p&gt;しかし、長くメンテしていくようなコードベースを一から書かせるということにはまだ使えない。この辺りはモデルの進歩を待つとともに、 &lt;code&gt;CLAUDE.md&lt;&#x2F;code&gt; で指示を作り込んだり、MCP サーバーとうまく連携させたりしていくことが必要そう。&lt;&#x2F;p&gt;
&lt;p&gt;多少の開発の知識があれば自分専用のアプリを簡単に作れる時代になってきているなぁと思う。人々が紹介している tips を取り入れてもっと遊んでみたい。&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>SageMaker Training にジョブを投げる君を作った</title>
		<published>2025-06-01T00:00:00+00:00</published>
		<updated>2025-06-01T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2025/sagemaker-training-tool/"/>
		<id>https://yng87.page/blog/2025/sagemaker-training-tool/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2025/sagemaker-training-tool/">&lt;p&gt;プライベートで機械学習実験をする際、特に一時的に大量のメモリや GPU が必要な場合、皆さんはどんな環境を使っているだろうか？Google colaboratory や Kaggle のような構築済みのノートブック環境を使う人もいれば、AWS・Google Cloud や RunPod のような何らかのクラウド環境にインスタンスを自前で立てている人もいるだろう。リッチな人は自宅に GPU の乗った強いマシンを持っているかもしれない。&lt;&#x2F;p&gt;
&lt;p&gt;自分は最近そのような用途に AWS の &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.aws.amazon.com&#x2F;ja_jp&#x2F;sagemaker&#x2F;latest&#x2F;dg&#x2F;how-it-works-training.html&quot;&gt;SageMaker Training&lt;&#x2F;a&gt; が便利だなと思っている。&lt;&#x2F;p&gt;
&lt;p&gt;SageMaker Training は機械学習の実験ジョブを AWS 上で実行するサービスである。ただし実態としては、指定したコンテナ上で任意の Python スクリプトを実行できるサービスと言うのが正しい。起動するインスタンスの種類や動かすコンテナイメージを指定し、実験スクリプトをアップロードすると、SageMaker が自動でインスタンスを立て、そこにスクリプトをダウンロードして実行する。成果物は S3 などの外部ストレージに保存してくれる。終了後はインスタンスを勝手に落としてくれるので、消し忘れで高額請求が来るような心配はない。AWS なので RunPod などに比べると GPU は高いが、一応スポットインスタンスも使えるので安めに済ませることもできる。&lt;&#x2F;p&gt;
&lt;p&gt;コンテナをバッチ的に実行してくれるサービスであれば、AWS Batch や Vertex Pipelines など色々な選択肢がある。その中でも SageMaker Training が使いやすいのは以下の点にあると思う&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;コンテナイメージの指定と Python ライブラリの指定を分離できる。実験スクリプトと一緒に &lt;code&gt;requirements.txt&lt;&#x2F;code&gt; を入れておくと、SageMaker がコンテナを起動した後に自動で必要なライブラリをインストールしてくれる。ライブラリを追加・変更するたびに、docker build して push するというようなことをしなくて良い。&lt;&#x2F;li&gt;
&lt;li&gt;同様なことは &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;cloud.google.com&#x2F;vertex-ai&#x2F;docs&#x2F;training&#x2F;create-python-pre-built-container&quot;&gt;Vertex AI のカスタムトレーニングでもできる&lt;&#x2F;a&gt;が、以前試した感じだとこちらは使えるイメージに縛りがあったりして思ったほど柔軟ではなかった。SageMaker Training は SDK が入ったイメージであれば基本何でも良いので便利。&lt;&#x2F;li&gt;
&lt;li&gt;必要なクラウドリソースは AWS 上の S3 バケットと SageMaker の実行ロールくらいなので準備が簡単。例えば AWS Batch は事前に ECS クラスタ（コンピューティング環境）を作る必要があり、その辺の適切な IAM ロールを準備するのが結構面倒。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;欠点はドキュメントがわかりづらく、入出力データのパスの渡し方などの独特の convention に慣れるのに時間がかかること。この点に関しては以下の資料が参考になった。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;pages.awscloud.com&#x2F;rs&#x2F;112-TZM-766&#x2F;images&#x2F;202205_AWS_Black_Belt_AIML_Dark_01_AmazonSageMakerTraining.pdf&quot;&gt;Amazon SageMaker Training (座学編) - 機械学習のモデル開発の試行錯誤を簡単にする&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;SageMaker Training を使う際は基本的には Python SDK を経由するのが便利。そこで最近は SDK をラップしたシンプルな CLI を作ってジョブを投げている。&lt;&#x2F;p&gt;



  



  



  


&lt;div class=&quot;github-card&quot;&gt;
  &lt;a class=&quot;github-card__link&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;yng87&amp;#x2F;smt&amp;#x2F;&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;
    &lt;div class=&quot;github-card__badge&quot; aria-hidden=&quot;true&quot;&gt;
      &lt;svg xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;20&quot; height=&quot;20&quot;&gt;&lt;path d=&quot;M5.88401 18.6531C5.58404 18.4523 5.32587 18.1972 5.0239 17.8366C4.91473 17.7062 4.47283 17.1521 4.55811 17.258C4.09533 16.6831 3.80296 16.4168 3.50156 16.3087C2.9817 16.1223 2.7114 15.5497 2.89784 15.0298C3.08428 14.51 3.65685 14.2397 4.17672 14.4261C4.92936 14.696 5.43847 15.1609 6.12425 16.0141C6.03025 15.8972 6.46364 16.4408 6.55731 16.5526C6.74784 16.7802 6.88732 16.918 6.99629 16.9909C7.20118 17.128 7.58451 17.1871 8.14709 17.1308C8.17065 16.7487 8.24136 16.378 8.34919 16.0355C5.38097 15.3101 3.70116 13.3949 3.70116 9.63947C3.70116 8.4006 4.0704 7.28368 4.75917 6.34756C4.5415 5.45368 4.57433 4.37259 5.06092 3.15611C5.1725 2.87715 5.40361 2.66314 5.69031 2.57328C5.77242 2.54949 5.81791 2.5389 5.89878 2.52648C6.70167 2.40319 7.83573 2.69681 9.31449 3.62311C10.181 3.41855 11.0885 3.31476 12.0012 3.31476C12.9129 3.31476 13.8196 3.41835 14.6854 3.62253C16.1619 2.68976 17.2986 2.39625 18.1072 2.52627C18.1919 2.53988 18.2645 2.55758 18.3249 2.57741C18.6059 2.66967 18.8316 2.88155 18.9414 3.15611C19.4279 4.37232 19.4608 5.45319 19.2433 6.34695C19.9342 7.28313 20.3012 8.39184 20.3012 9.63947C20.3012 13.3966 18.627 15.3046 15.6588 16.0318C15.7837 16.4467 15.8496 16.9103 15.8496 17.4118C15.8496 18.0763 15.8471 18.7108 15.8424 19.4223C15.8412 19.6124 15.8397 19.8156 15.8375 20.1279C16.2129 20.2107 16.5229 20.5074 16.6031 20.9086C16.7114 21.4502 16.3602 21.977 15.8186 22.0853C14.6794 22.3132 13.8353 21.5535 13.8353 20.5608C13.8353 20.4705 13.836 20.3414 13.8375 20.1142C13.8398 19.8012 13.8412 19.5987 13.8425 19.4092C13.8471 18.7017 13.8496 18.0714 13.8496 17.4118C13.8496 16.7145 13.6664 16.26 13.4237 16.0508C12.7627 15.481 13.0977 14.3971 13.965 14.2996C16.9314 13.9663 18.3012 12.8174 18.3012 9.63947C18.3012 8.68484 17.9893 7.89547 17.3881 7.23534C17.1301 6.95209 17.0567 6.54634 17.199 6.19062C17.3647 5.77639 17.4354 5.2336 17.2941 4.57678L17.2847 4.57944C16.7928 4.71861 16.1744 5.01956 15.4261 5.52826C15.182 5.69413 14.8772 5.74377 14.5932 5.66388C13.7729 5.43319 12.8913 5.31476 12.0012 5.31476C11.111 5.31476 10.2294 5.43319 9.40916 5.66388C9.12662 5.74335 8.82344 5.69468 8.57997 5.53077C7.8274 5.02414 7.2056 4.72355 6.71079 4.58352C6.56735 5.23672 6.63814 5.77758 6.80336 6.19062C6.94565 6.54634 6.87219 6.95209 6.61423 7.23534C6.01715 7.89096 5.70116 8.69352 5.70116 9.63947C5.70116 12.8114 7.07225 13.9681 10.023 14.2996C10.8883 14.3969 11.2246 15.4767 10.5675 16.048C10.3751 16.2153 10.1384 16.7799 10.1384 17.4118V20.5608C10.1384 21.5472 9.30356 22.2866 8.17878 22.0898C7.63476 21.9946 7.27093 21.4764 7.36613 20.9324C7.43827 20.5201 7.75331 20.2114 8.13841 20.1274V19.1379C7.22829 19.1991 6.47656 19.0496 5.88401 18.6531Z&quot; fill=&quot;currentColor&quot;&gt;&lt;&#x2F;path&gt;&lt;&#x2F;svg&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;github-card__content&quot;&gt;
      &lt;div class=&quot;github-card__repo&quot;&gt;yng87&amp;#x2F;smt&lt;&#x2F;div&gt;
      
      &lt;div class=&quot;github-card__description&quot;&gt;SageMaker Training のジョブ投入を簡単にするためのシンプルな CLI ラッパー。&lt;&#x2F;div&gt;
      
      
      &lt;div class=&quot;github-card__meta&quot;&gt;Python&lt;&#x2F;div&gt;
      
    &lt;&#x2F;div&gt;
  &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;開発のワークフローとしては以下のような感じになる&lt;&#x2F;p&gt;
&lt;ol start=&quot;0&quot;&gt;
&lt;li&gt;SageMaker Training に必要なリソース（S3 バケット・IAM ロール・Docker イメージ）を準備する。&lt;&#x2F;li&gt;
&lt;li&gt;ローカルで実験スクリプトを開発する。小規模なデータセットを用いてローカルで動作確認をする。依存管理には &lt;code&gt;uv&lt;&#x2F;code&gt; を使う。&lt;&#x2F;li&gt;
&lt;li&gt;使用するライブラリに変更があった場合は、&lt;code&gt;uv export -o requirements.txt --no-hashes&lt;&#x2F;code&gt; で &lt;code&gt;requirements.txt&lt;&#x2F;code&gt; を作る。&lt;&#x2F;li&gt;
&lt;li&gt;上記 CLI &lt;code&gt;smt&lt;&#x2F;code&gt; を実行する。するとカレントディレクトリが &lt;code&gt;tar.gz&lt;&#x2F;code&gt; が固められて S3 にアップロードされ、 SageMaker Training のジョブがキックされる。&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;ローカルで開発したスクリプトをそのままリモートのコンテナ環境で実行できるので結構便利な仕組みだと思っている。&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>なぜ機械学習システムの評価をするのか&#x2F;評価駆動開発について</title>
		<published>2025-05-02T00:00:00+00:00</published>
		<updated>2025-05-02T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2025/why-eval/"/>
		<id>https://yng87.page/blog/2025/why-eval/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2025/why-eval/">&lt;h2 id=&quot;hazimeni&quot;&gt;はじめに&lt;&#x2F;h2&gt;
&lt;p&gt;ここ最近フォローしている人々が機械学習、特に LLM を使ったシステム開発における評価について話しているのでまとめておきたいと思う。主に参考にしたのは以下の記事やブログ：&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.deeplearning.ai&#x2F;the-batch&#x2F;issue-297&#x2F;&quot;&gt;The Batch Issue 297&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;eugeneyan.com&#x2F;writing&#x2F;eval-process&#x2F;&quot;&gt;An LLM‑as‑Judge Won&#x27;t Save The Product—Fixing Your Process Will | eugeneyan&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.oreilly.com&#x2F;library&#x2F;view&#x2F;ai-engineering&#x2F;9781098166298&#x2F;&quot;&gt;AI Engineering | Chip Huyen&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;ここで「評価」という言葉で指すのはオフラインにおける自動化された評価であって、人間の専門家によるアノテーションや、オンライン A&#x2F;B テストのような実行コストが高いもののことではないということをはじめに断っておく。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;nazeping-jia-gabi-yao-ka&quot;&gt;なぜ評価が必要か&lt;&#x2F;h2&gt;
&lt;p&gt;上に挙げた記事はどれも皆、自動評価は機械学習を使ったアプリケーションを開発する際のイテレーションを高速化するための手段だと言っている。あらかじめ開発フローに評価を組み込んでおき、評価値の良し悪しを見て試行錯誤を繰り返して高速にシステムを改善していくわけである。このやり方を最近は評価駆動開発と呼んだりもしている。&lt;&#x2F;p&gt;
&lt;p&gt;評価駆動開発は言葉としては新しいものの、実務で機械学習を使ったサービス開発に携わってきた人にとっては身近な考え方だと思う。例えば、推薦システムや検索システムでは出力例を人間がいくつか見たところで&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#vibe-check&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; 良し悪しを判断するのは難しいので、何らかのオフライン評価指標を設定してそれを最適化するように試行錯誤したのち、AB テストなどでユーザーからのフィードバックを得るのが一般的である。&lt;&#x2F;p&gt;
&lt;p&gt;また、個人的には自動評価はシステムのメンテナンス性のためにも重要だと思っている。担当者が離れた後で、なんのオフライン評価もされていない機械学習システムが残っていると、そこで使われているモデルを変更するコストが跳ね上がる。特に LLM を使っている場合は発展のスピードが速いので、変更容易性を担保するのはより重要である。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ping-jia-hahe-dehanaika&quot;&gt;評価は何ではないか&lt;&#x2F;h2&gt;
&lt;p&gt;最近は LLM-as-judge という手法も広まっており、これまでは難しかった観点に関してもある程度評価値を算出することができるようになっている。しかし、現在の LLM の性能で人間の介入を排した完全な自動評価を実現できると考えている人は少ないように見える。&lt;&#x2F;p&gt;
&lt;p&gt;またシステムによる自動評価はあくまで相対評価として役立つものであって、システムの性能の絶対的なレベルを測定するものだと捉えることは少ないように思う。この点については &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.deeplearning.ai&#x2F;the-batch&#x2F;issue-297&#x2F;&quot;&gt;Andrew Ngが以下のように述べている&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;To me, a successful eval meets the following criteria. Say, we currently have system A, and we might tweak it to get a system B:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;If A works significantly better than B according to a skilled human judge, the eval should give A a significantly higher score than B.&lt;&#x2F;li&gt;
&lt;li&gt;If A and B have similar performance, their eval scores should be similar.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;オフライン評価はあくまで開発高速化のための安価な proxy なので、真の良し悪しとの大雑把な順序相関があれば十分ということである。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#eval-book&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;この辺の評価に対する態度は分野によっても異なっていて、特にバイアスのないデータセットを集めるのが難しい推薦や検索のような分野では、モデルの学習が期待通り進んだかどうかというヘルスチェック程度にしか扱われないこともある。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#example&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ping-jia-wodonoyounikai-fa-saikurunizu-miip-muka&quot;&gt;評価をどのように開発サイクルに組み込むか&lt;&#x2F;h2&gt;
&lt;p&gt;冒頭の記事では、評価の構築それ自体も iterative な process であり、人手による評価を時々挟むことで、自動評価と人手の評価の相関をより良くするように継続的に改善していくことが必要だと述べられている。本当のゴールはユーザーにとって良いものを作ることであり、自動評価はあくまでそのためのツールなので、真のゴールを達成できるように指標やデータセットを調整していくということである。この点は実務と機械学習コンペの大きな違いだと思う。&lt;&#x2F;p&gt;
&lt;p&gt;また、システムの failure mode を把握することも重要である。無闇にトップダウンで指標を設定するのではなく、実際のシステムの挙動やユーザーからのフィードバックなどのデータを見て失敗箇所を把握し、それを定量化するために評価を導入するのが良い。特定の課題を念頭に置かずシステムを包括的に評価してくれるような銀の弾丸はない。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;zui-hou-ni&quot;&gt;最後に&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.oreilly.com&#x2F;library&#x2F;view&#x2F;ai-engineering&#x2F;9781098166298&#x2F;&quot;&gt;AI Engineering&lt;&#x2F;a&gt; は評価駆動開発をかなり推しているが、その一方で、評価しやすい指標にばかり目を向けると真に革新的なアイデアを見落とすという、&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Streetlight_effect&quot;&gt;街頭効果&lt;&#x2F;a&gt;に警鐘を鳴らしてもいる。ゲームチェンジャーとなるようなアイディアは容易に評価できないかもしれないので、評価一辺倒になりすぎないようにもすべきである。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;vibe-check&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;最近はこういうやり方を vibe check と呼ぶらしい。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;eval-book&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;同様の議論は&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;gihyo.jp&#x2F;book&#x2F;2023&#x2F;978-4-297-13314-6&quot;&gt;評価指標入門&lt;&#x2F;a&gt;という本にも載っている。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;example&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;gihyo.jp&#x2F;book&#x2F;2023&#x2F;978-4-297-13314-6&quot;&gt;評価指標入門&lt;&#x2F;a&gt;で取り上げられている Booking.com の事例。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>テーブルデータの前処理を何でやるか</title>
		<published>2025-02-17T00:00:00+00:00</published>
		<updated>2025-02-17T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2025/howto-preprocess-tabular-data/"/>
		<id>https://yng87.page/blog/2025/howto-preprocess-tabular-data/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2025/howto-preprocess-tabular-data/">&lt;p&gt;最近読んだ、&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;substack.com&#x2F;home&#x2F;post&#x2F;p-154253264&quot;&gt;The Rise of Single-Node Processing: Challenging the Distributed-First Mindset&lt;&#x2F;a&gt; という記事に最近考えていたことが書いてあったので便乗して自分の考えを書き留めておく。&lt;&#x2F;p&gt;
&lt;p&gt;元記事ではかつては大規模なデータの処理というと何はともあれ分散システムであり、Spark や BigQuery などを導入するのが当然であったが、近年は &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;duckdb.org&#x2F;&quot;&gt;DuckDB&lt;&#x2F;a&gt; や &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;pola.rs&#x2F;&quot;&gt;Polars&lt;&#x2F;a&gt; などシングルノードでも高速にテーブルデータを処理できる技術が登場してきたことで必ずしも分散システムは必要ではないよねという風潮に変わってきた、ということが述べられている。コスト面でもクラウドを使うのであれば、小さいインスタンスをいくつも立てて分散処理するのと、合計して同程度の vCPU や RAM を持つ一つの大きなインスタンスを立てて処理するのとで料金が劇的に変わるわけではないため、オーバーヘッドの少ないシングルノードを選択するのが理にかなっているとのこと。&lt;&#x2F;p&gt;
&lt;p&gt;もちろんそもそもシングルノードには絶対に乗らないような規模のデータを扱う組織もあるとは思うが、何年か前に &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;motherduck.com&#x2F;blog&#x2F;big-data-is-dead&#x2F;&quot;&gt;Big Data is Dead&lt;&#x2F;a&gt; という BigQuery の開発者の記事で指摘されていたように、実はそういう組織はそこまで多くない。大規模データを持っている企業が限られるし、持っていたとしても大抵の分析案件は直近データだけクエリすれば十分なことが多いからだ。&lt;&#x2F;p&gt;
&lt;p&gt;以上の話はおおむね分析やそのためのデータマート構築の視点で書かれていると思うが、機械学習モデルを作る際のテーブルデータの前処理についても同様のことが当てはまると思っている。&lt;&#x2F;p&gt;
&lt;p&gt;自分がこの業界に入り最初に入社した会社では、BigQuery 上に DWH が構築されており、データサイエンティストは SQL を書いてモデルの学習に必要なデータを抽出していた。BigQuery の真っ当なユースケースとしては上に述べたような分析・データマート構築というところになると思うが、その強力なスケーラビリティから機械学習モデリングのワークフローに組み込まれ、生ログの整形・ラベリング・計算された特徴量の GCS への出力まで担う場面も（自分の観測してきた範囲では）非常に多い。当時所属していたチームでも、テーブルデータの前処理は BigQuery でやるのがベストプラクティスとされていた。&lt;&#x2F;p&gt;
&lt;p&gt;今の会社に移ってからも当初はそのようなやり方でテーブルデータを扱っていたのだが、主に以下の二点が理由で最近は BigQuery でデータを処理しすぎないほうが良いと感じる場面が増えてきた&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;Polars のように、シングルノードでも大規模なデータを簡単に高速に処理できるライブラリが登場した&lt;&#x2F;li&gt;
&lt;li&gt;ソフトウェアエンジニアリングの経験を積んだことで、コードや ML パイプラインの品質をより重視するようになった&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;一点目に関しては数年前から散々話題になっていると思うが、やはり Polars が性能面で非常に優れていて、適当にクエリを書いて Pandas の 1&#x2F;10 くらいの時間で処理できるのだからもう Pandas には戻れない。また Pandas の API は歴史的な経緯で荒れ果てていると個人的に思っているが、Polars は新興のライブラリのこともあってその辺は綺麗に保たれていると感じる。&lt;&#x2F;p&gt;
&lt;p&gt;二点目については、SQL で凝った処理を書くととても見通しが悪いし、処理を小さな関数に分けて使い回せないし、何よりユニットテストを気軽に書くということが難しい。大体は SQL をコンソールに貼って実行して結果を目で確認して意図通りっぽいかということを調べていくことになる。過去の現場でテストの書かれていない複雑なデータセット生成用 SQL をいくつも見てきて非常に辛い思いをしたが、当時はそれは仕方のないことだと思っていたし、自分もそのような SQL をいくつも生成してしまっていた。ユニットテストを書くのは大事だ。機械学習の実験フェーズにおいてもデータパイプラインに最低限のテストを書いておくと早めにバグやリークを潰せるし、本番移行のためにリファクタリングをするぞとなったときもひとまずそのテストを信頼して移植することが出来る。&lt;&#x2F;p&gt;
&lt;p&gt;以上の点を踏まえての個人的なベストプラクティスとしては、&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;DWH に SQL でクエリする際にはあまり複雑なロジックを入れず、&lt;code&gt;where&lt;&#x2F;code&gt; で指定できるフィルタや、ちょっとしたメタデータの join に留める&lt;&#x2F;li&gt;
&lt;li&gt;集約特徴量などの複雑になりがちな計算は Polars で書いてユニットテストをちゃんとつける&lt;&#x2F;li&gt;
&lt;li&gt;シングルノードでの処理がメモリや実行時間的に厳しい場合は上の縛りを緩める&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;という感じだ。&lt;&#x2F;p&gt;
&lt;p&gt;それから話の前提として、チームにはメモリや CPU を柔軟に指定できるリモートのジョブ実行環境がすでにあるとしている。たとえば AWS Batch や Google Cloud の Vertex Pipelines のようなイメージで、Polars のジョブはそこに投げる。これがない場合はそもそも実験の試行錯誤がスケールしないので、まずはそちらを整備したほうが良いだろう。&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>XGBoost と LightGBM に実装されているポジションバイアス除去を試してみた</title>
		<published>2024-08-08T00:00:00+00:00</published>
		<updated>2024-08-08T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2024/gbdt-position-debias/"/>
		<id>https://yng87.page/blog/2024/gbdt-position-debias/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2024/gbdt-position-debias/">&lt;h2 id=&quot;hazimeni&quot;&gt;はじめに&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;yng87.page&#x2F;blog&#x2F;2024&#x2F;impl-click-modeling&#x2F;&quot;&gt;以前も記事にしたが&lt;&#x2F;a&gt;、ウェブ上のユーザーの行動ログを使って推薦システムを開発している自分のような ML エンジニアにとって、ランキング学習におけるポジションバイアスの除去は重要なテーマである。サービスのログは通常様々なバイアスに塗れており、特にリストの上位に表示されたアイテムほどクリックが集まりやすくなってしまうポジションバイアスは非常に厄介だ。アカデミアではこの手のテーマはだいぶ研究が進んでいるものの、これまでは論文や書籍で手法が紹介されるだけで、手軽にパッと使えるライブラリは存在しなかった。&lt;&#x2F;p&gt;
&lt;p&gt;しかしどうやら最近になって XGBoost や LightGBM という多くの人が使う強力な GBDT ライブラリにポジションバイアスを除去する機能が実装されたらしく、これが使い物になるのであれば実務で利用するハードルがグッと下がると思い、実験して性能を検証してみた。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jian-zheng-nishi-udetasetuto&quot;&gt;検証に使うデータセット&lt;&#x2F;h2&gt;
&lt;p&gt;ここではランキング学習の評価では定番である &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.microsoft.com&#x2F;en-us&#x2F;research&#x2F;project&#x2F;mslr&#x2F;&quot;&gt;MSLR-WEB30K&lt;&#x2F;a&gt; を加工してポジションバイアスのあるクリックデータセットを作成した。具体的なポジションの付与方法や、クリックラベルの生成方法は&lt;a href=&quot;https:&#x2F;&#x2F;yng87.page&#x2F;blog&#x2F;2024&#x2F;impl-click-modeling&#x2F;&quot;&gt;以前書いた記事&lt;&#x2F;a&gt;と同様なのでそちらを参照してほしい。&lt;&#x2F;p&gt;
&lt;p&gt;このデータセットでは、ポジションと（バイアスのない真の）relevance との交絡度合いをパラメータ $w$ によって調整している。$w=0$ であれば、ポジションの値は relevance とは関係なく決まり、$w=1$ であれば relevance によって完全に決まる（最も relevance の高いアイテムを一番上に配置する）ようになっている。そしてサービスのログとして得られるクリックは、(ユーザーにそのアイテムが表示される確率)×(ユーザーがその表示されたアイテムを気に入る確率) によって発生する。&lt;&#x2F;p&gt;
&lt;p&gt;我々が本当にモデリングしたいのは、後者の「ユーザーがその表示されたアイテムを気に入る確率」であるが、クリックログからはそれを単独で切り出すことができないというのがこの問題の難しいところである。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;pozisiyonbaiasuchu-qu-noshi-zhuang-fang-fa&quot;&gt;ポジションバイアス除去の実装方法&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;xgboost&quot;&gt;XGBoost&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;xgboost.readthedocs.io&#x2F;en&#x2F;stable&#x2F;tutorials&#x2F;learning_to_rank.html#position-bias&quot;&gt;XGBoostでは version 2.0.0 からポジションバイアスに対処できる手法である Unbiased LambdaMART が実装された&lt;&#x2F;a&gt;。このアルゴリズム自体は以下の ByteDance の論文で提案されており、傾向スコアベースの手法になっている。論文ではこれまで提案されてきた pointwise 損失に対する IPW 法を pairwise 損失に拡張し、また傾向スコアを前もって求めるのではなく木の学習と同時に推定する手法を提案している。そして実際のサービス上で AB テストを実施して有効性を確かめたということまで書かれている。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#product&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;1809.05818&quot;&gt;[1809.05818] Unbiased LambdaMART: An Unbiased Pairwise Learning-to-Rank Algorithm&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;実際のコード例は &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;xgboost.readthedocs.io&#x2F;en&#x2F;stable&#x2F;python&#x2F;examples&#x2F;learning_to_rank.html#sphx-glr-python-examples-learning-to-rank-py&quot;&gt;XGBoostのドキュメント&lt;&#x2F;a&gt; に一応あるが非常にわかりづらい。サンプルコード内でデータセットを用意するのに使われている &lt;code&gt;sort_ltr_samples&lt;&#x2F;code&gt; という関数の実装を読みに行くと、各クエリグループの中でポジションの昇順にソートしているようである。なので例えば pandas でデータセットを作成する際には以下のような感じでソートする必要がある。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;df_train&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; df_train&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;sort_values&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;by&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;query&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;position&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;X_train&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; df_train&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;features&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;y_train&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; df_train&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;click&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;group_train&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; df_train&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;group&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;モデル学習時には、&lt;code&gt;lambdarank_unbiased&lt;&#x2F;code&gt; で unbiased LambdaMART を使うよう指定する。また &lt;code&gt;lambdarank_bias_norm&lt;&#x2F;code&gt; で傾向スコア部分に対する正則化度合いを調整できる。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; xgboost&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; as&lt;&#x2F;span&gt;&lt;span&gt; xgb&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; xgb&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;XGBRanker&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    lambdarank_unbiased&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;True&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    lambdarank_bias_norm&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;fit&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    X_train&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    y_train&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    qid&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;group_train&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    eval_set&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (&lt;&#x2F;span&gt;&lt;span&gt;X_train&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; y_train&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        (&lt;&#x2F;span&gt;&lt;span&gt;X_val&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; y_val&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    ]&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    eval_qid&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;group_train&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; group_val&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;lightgbm&quot;&gt;LightGBM&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;lightgbm.readthedocs.io&#x2F;en&#x2F;latest&#x2F;Advanced-Topics.html#support-for-position-bias-treatment&quot;&gt;LightGBM でも最近ポジションバイアスに対処する機能が実装されている&lt;&#x2F;a&gt;。IPW を採用していた XGBoost とは異なり、LightGBM では一般化加法モデルベースの手法を採用している。これは学習時には $s(x, pos) = f(x) + g(pos)$ という和の形でデータをフィットし ($x$ はポジションを含まないモデルの特徴量)、ポジションの効果を $g$ に押し付けた上で推論時には $f$ だけを利用してスコアリングするという手法である。例えば以下の論文などではニューラルネットワークモデルでこのアイディアを実装して有効性を示している。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2212.13937&quot;&gt;[2212.13937] Towards Disentangling Relevance and Bias in Unbiased Learning to Rank&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;この手法については&lt;a href=&quot;https:&#x2F;&#x2F;yng87.page&#x2F;blog&#x2F;2024&#x2F;impl-click-modeling&#x2F;&quot;&gt;以前も記事にした&lt;&#x2F;a&gt;のでそちらも参照してほしい。&lt;&#x2F;p&gt;
&lt;p&gt;こちらもドキュメントがわかりづらいが、要はデータセットを作る際にポジションの値を array として渡せばそれでバイアス除去がオンになるようである。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; lightgbm&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; as&lt;&#x2F;span&gt;&lt;span&gt; lgb&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ds_train&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; lgb&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;Dataset&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    X_train&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    label&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;y_train&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    group&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;group_train&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable&quot;&gt;    position&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;position_train&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;model&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; lgb&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;train&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;model_params&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ds_train&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;また、&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;lightgbm.readthedocs.io&#x2F;en&#x2F;latest&#x2F;Parameters.html#lambdarank_position_bias_regularization&quot;&gt;&lt;code&gt;lambdarank_position_bias_regularization&lt;&#x2F;code&gt; というパラメータでポジションバイアス因子（上の $g$）の強さを調整できる&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yan-jie-guo&quot;&gt;実験結果&lt;&#x2F;h2&gt;
&lt;p&gt;上で MSLR-WEB30K から作成したデータセットに対して、普通の XGBoost・LightGBM とそのポジションバイアス除去版の性能を比較した。評価指標は NDCG@5 で、学習はバイアスのあるクリックラベルについて行い、評価時は真に予測したい unbiased な relevance ラベルについて評価した。&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: left&quot;&gt;$w$&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;XGBoost&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Debiased XGBoost&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;LightGBM&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;Debiased LightGBM&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;0&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2683&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2881&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2748&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;0.3115&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;0.2&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2964&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2783&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.3047&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;0.3212&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;0.6&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.3057&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2963&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.3179&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;0.3362&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;0.8&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.3124&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2782&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2997&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;0.3414&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;1&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2927&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2905&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.3025&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;0.3414&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;これを見ると、一貫して LightGBM のポジション除去機能が高い性能を示していることがわかった。&lt;&#x2F;p&gt;
&lt;p&gt;コードは以下。&lt;&#x2F;p&gt;



  



  



  


&lt;div class=&quot;github-card&quot;&gt;
  &lt;a class=&quot;github-card__link&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;yng87&amp;#x2F;unbiased-ltr&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;
    &lt;div class=&quot;github-card__badge&quot; aria-hidden=&quot;true&quot;&gt;
      &lt;svg xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;20&quot; height=&quot;20&quot;&gt;&lt;path d=&quot;M5.88401 18.6531C5.58404 18.4523 5.32587 18.1972 5.0239 17.8366C4.91473 17.7062 4.47283 17.1521 4.55811 17.258C4.09533 16.6831 3.80296 16.4168 3.50156 16.3087C2.9817 16.1223 2.7114 15.5497 2.89784 15.0298C3.08428 14.51 3.65685 14.2397 4.17672 14.4261C4.92936 14.696 5.43847 15.1609 6.12425 16.0141C6.03025 15.8972 6.46364 16.4408 6.55731 16.5526C6.74784 16.7802 6.88732 16.918 6.99629 16.9909C7.20118 17.128 7.58451 17.1871 8.14709 17.1308C8.17065 16.7487 8.24136 16.378 8.34919 16.0355C5.38097 15.3101 3.70116 13.3949 3.70116 9.63947C3.70116 8.4006 4.0704 7.28368 4.75917 6.34756C4.5415 5.45368 4.57433 4.37259 5.06092 3.15611C5.1725 2.87715 5.40361 2.66314 5.69031 2.57328C5.77242 2.54949 5.81791 2.5389 5.89878 2.52648C6.70167 2.40319 7.83573 2.69681 9.31449 3.62311C10.181 3.41855 11.0885 3.31476 12.0012 3.31476C12.9129 3.31476 13.8196 3.41835 14.6854 3.62253C16.1619 2.68976 17.2986 2.39625 18.1072 2.52627C18.1919 2.53988 18.2645 2.55758 18.3249 2.57741C18.6059 2.66967 18.8316 2.88155 18.9414 3.15611C19.4279 4.37232 19.4608 5.45319 19.2433 6.34695C19.9342 7.28313 20.3012 8.39184 20.3012 9.63947C20.3012 13.3966 18.627 15.3046 15.6588 16.0318C15.7837 16.4467 15.8496 16.9103 15.8496 17.4118C15.8496 18.0763 15.8471 18.7108 15.8424 19.4223C15.8412 19.6124 15.8397 19.8156 15.8375 20.1279C16.2129 20.2107 16.5229 20.5074 16.6031 20.9086C16.7114 21.4502 16.3602 21.977 15.8186 22.0853C14.6794 22.3132 13.8353 21.5535 13.8353 20.5608C13.8353 20.4705 13.836 20.3414 13.8375 20.1142C13.8398 19.8012 13.8412 19.5987 13.8425 19.4092C13.8471 18.7017 13.8496 18.0714 13.8496 17.4118C13.8496 16.7145 13.6664 16.26 13.4237 16.0508C12.7627 15.481 13.0977 14.3971 13.965 14.2996C16.9314 13.9663 18.3012 12.8174 18.3012 9.63947C18.3012 8.68484 17.9893 7.89547 17.3881 7.23534C17.1301 6.95209 17.0567 6.54634 17.199 6.19062C17.3647 5.77639 17.4354 5.2336 17.2941 4.57678L17.2847 4.57944C16.7928 4.71861 16.1744 5.01956 15.4261 5.52826C15.182 5.69413 14.8772 5.74377 14.5932 5.66388C13.7729 5.43319 12.8913 5.31476 12.0012 5.31476C11.111 5.31476 10.2294 5.43319 9.40916 5.66388C9.12662 5.74335 8.82344 5.69468 8.57997 5.53077C7.8274 5.02414 7.2056 4.72355 6.71079 4.58352C6.56735 5.23672 6.63814 5.77758 6.80336 6.19062C6.94565 6.54634 6.87219 6.95209 6.61423 7.23534C6.01715 7.89096 5.70116 8.69352 5.70116 9.63947C5.70116 12.8114 7.07225 13.9681 10.023 14.2996C10.8883 14.3969 11.2246 15.4767 10.5675 16.048C10.3751 16.2153 10.1384 16.7799 10.1384 17.4118V20.5608C10.1384 21.5472 9.30356 22.2866 8.17878 22.0898C7.63476 21.9946 7.27093 21.4764 7.36613 20.9324C7.43827 20.5201 7.75331 20.2114 8.13841 20.1274V19.1379C7.22829 19.1991 6.47656 19.0496 5.88401 18.6531Z&quot; fill=&quot;currentColor&quot;&gt;&lt;&#x2F;path&gt;&lt;&#x2F;svg&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;github-card__content&quot;&gt;
      &lt;div class=&quot;github-card__repo&quot;&gt;yng87&amp;#x2F;unbiased-ltr&lt;&#x2F;div&gt;
      
      &lt;div class=&quot;github-card__description&quot;&gt;Reproduction scripts for unbiased learning-to-rank methods&lt;&#x2F;div&gt;
      
      
      &lt;div class=&quot;github-card__meta&quot;&gt;Python&lt;&#x2F;div&gt;
      
    &lt;&#x2F;div&gt;
  &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;h2 id=&quot;owarini&quot;&gt;おわりに&lt;&#x2F;h2&gt;
&lt;p&gt;今回の結果はあくまで人工的にポジションを生成したデータセットにおけるもので、現実のデータではポジションバイアスがもっと複雑な入り方をしている場合もあるので注意が必要ではあるだろう。それでも LightGBM に position を記録した array を渡すだけで手軽に利用できるのはとてもありがたく、これまでよりも気軽に実装して AB テスト等にかけることができそうだ。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;product&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;ByteDance なので TikTok かと思ったが、Toutiao というニュースプラットフォームでの AB テストらしい。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>NetflixのSequential ABテスト論文を読む</title>
		<published>2024-05-05T00:00:00+00:00</published>
		<updated>2024-05-05T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2024/netflix-sequential-abtesting/"/>
		<id>https://yng87.page/blog/2024/netflix-sequential-abtesting/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2024/netflix-sequential-abtesting/">&lt;p&gt;最近 Netflix のテックブログを読んでいたら sequential AB testing についての話題があった。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;netflixtechblog.com&#x2F;sequential-a-b-testing-keeps-the-world-streaming-netflix-part-1-continuous-data-cba6c7ed49df&quot;&gt;Sequential A&#x2F;B Testing Keeps the World Streaming Netflix Part 1: Continuous Data | by Netflix Technology Blog | Netflix TechBlog&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;netflixtechblog.com&#x2F;sequential-testing-keeps-the-world-streaming-netflix-part-2-counting-processes-da6805341642&quot;&gt;Sequential A&#x2F;B Testing Keeps the World Streaming Netflix Part 2: Counting Processes | by Netflix Technology Blog | Netflix TechBlog&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Sequential AB testing というのは、実験中に継続的にメトリクスを監視し、treatment と control で有意差が出た瞬間に実験を停止しようというものだ。もちろん、普通の t 検定やカイ二乗検定を実行すると大変な多重検定になるので、有意差が出やすくなってしまう (Type I error が増加してしまう)。&lt;&#x2F;p&gt;
&lt;p&gt;そこで、実験のどの時刻でも Type I error が適切な制御下にあるような検定統計量を定義し、それに基づいて実験の早期打ち切りをできるようにしたというのが、冒頭のブログの趣旨である。これは論文になっていて、NeurIPS に採択もされたらしい。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2011.03567&quot;&gt;[2011.03567] Anytime-Valid Inference for Multinomial Count Data&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;「有意差が出た瞬間に実験を打ち切って良い」というのは実務家としてはとても便利な性質なので、この論文をざっくり理解してみようと思い、このブログ記事を書いてみた。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jian-ding-tong-ji-liang&quot;&gt;検定統計量&lt;&#x2F;h2&gt;
&lt;p&gt;今回対象とするのは離散的な「カウント」データである。例えば、EC サイトに何か新施策を実装してそれを treatment 群のユーザにだけ見えるようにし、サイトに訪問したユーザーが商品を購入したら、所属する群の conversion カウントが1インクリメントされるというような状況をイメージして欲しい。各時刻 $t$ で $\boldsymbol{n}_t = (n_{t,1}, n_{t,2}, \dots, n_{t,d}) $ という整数値のデータが得られる ($d$ は群の数で $n_{t,i}$ が群 $i$ でコンバージョンしたユーザー数)。&lt;&#x2F;p&gt;
&lt;p&gt;通常の AB テストであれば、あらかじめ必要なサンプルサイズを見積り期間を定めて実験し、実験終了後に一度だけ検定を行う。今回のカウントデータであれば、カイ二乗検定がよく使われるだろう。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#non-conversion&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;しかしここでは各時刻で毎回検定を行いたいのであった。上記論文に倣い、そのための検定統計量を構成しよう。&lt;&#x2F;p&gt;
&lt;p&gt;まず、帰無仮説 $M_0$ として以下のような多項分布から独立に生成される確率変数 $\boldsymbol{x}_i$ を考える
$$\begin{aligned}
\tag{1}
\boldsymbol{x}_1, \boldsymbol{x}_2, \dots | M_0 \sim \mathrm{Multinomial(\boldsymbol\theta_0)}
\end{aligned}$$
$\boldsymbol{x}_i$ は one-hot ベクトル形式であり、$\sim$ は i.i.d であるとする。$\boldsymbol{x}_i$ を足すと上のカウント $\boldsymbol{n}_t$ になると思ってもらえれば良い。&lt;&#x2F;p&gt;
&lt;p&gt;次に対立仮説 $M_1$ を設定する。ここでは両側検定をすることにし $\boldsymbol\theta \neq \boldsymbol\theta_0$ となる生成過程として以下を考える
$$\begin{aligned}
\tag{2}
\boldsymbol{x}_1, \boldsymbol{x}_2, \dots |\boldsymbol\theta ,M_1 &amp;amp;\sim \mathrm{Multinomial(\boldsymbol\theta)}\\ \notag
\boldsymbol\theta &amp;amp;\sim \mathrm{Dirichlet}(\boldsymbol\alpha_0)
\end{aligned}$$
Dirchlet 分布は連続値の分布であり、特定の $\boldsymbol\theta=\boldsymbol\theta_0$ を取る確率はゼロなので、対立仮説になっている。&lt;&#x2F;p&gt;
&lt;p&gt;そして以下のベイズファクターを定義しよう&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#prior&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;$$\begin{aligned}
O_n(\boldsymbol\theta_0)=BF_{10}(\boldsymbol{x}_{1:n})=\frac{p(\boldsymbol{x}_{1:n}|M_1)}{p(\boldsymbol{x}_{1:n}|M_0)}
\end{aligned}$$&lt;&#x2F;p&gt;
&lt;p&gt;ベイズファクターはベイズ統計の枠組みで検定を行う手法の一つであり、&lt;a href=&quot;https:&#x2F;&#x2F;yng87.page&#x2F;blog&#x2F;2023&#x2F;hypothesis-testing-freq-and-bayes&#x2F;&quot;&gt;以前も記事にした&lt;&#x2F;a&gt;ので参照してほしい。
このベイズファクターを解析的に扱える形にしよう。周辺尤度の計算は初等的な積分をすればよく $\boldsymbol{x}_i$ の各成分を足したものを $S^n_i=\sum_{j=1}^nx_{j,i}$, $\boldsymbol{S}_n = (S^n_1, \dots, S^n_d)$ と書くと&lt;&#x2F;p&gt;
&lt;p&gt;$$\begin{aligned}
\tag{3}
O_n(\boldsymbol\theta_0)=\frac{\mathrm{Beta}(\boldsymbol{\alpha}_0 + \boldsymbol{S}_n)}{\mathrm{Beta}(\boldsymbol{\alpha_0})}
\frac{1}{\boldsymbol\theta_0^{\boldsymbol{S}_n}}
\end{aligned}$$&lt;&#x2F;p&gt;
&lt;p&gt;となることがわかる。$\mathrm{Beta}$ は $d$ 次元のベータ関数で、$\boldsymbol\theta_0^{\boldsymbol{S}_n}$ は成分ごとに冪乗して成分間で積を取った量である。&lt;&#x2F;p&gt;
&lt;p&gt;このベイズファクター $O_n(\boldsymbol\theta_0)$ は逐次検定をするのに望ましい性質を持っている。詳しい証明は自分の能力を超えるので書けないが、冒頭の論文によると $O_n(\boldsymbol\theta_0)$ が nonnegative supermartingale になることを利用すると以下の性質を示すことができるらしい:&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;性質&lt;&#x2F;strong&gt;: $O_n(\boldsymbol\theta_0) \geq 1&#x2F;u$ となる $n$ が存在する確率は $u$ 以下である ($0\leq u \leq 1$)&lt;&#x2F;p&gt;
&lt;p&gt;そこでこのベイズファクターを利用した &lt;strong&gt;sequential p-value&lt;&#x2F;strong&gt; を&lt;&#x2F;p&gt;
&lt;p&gt;$$\begin{aligned}
p_n = \mathrm{min}(p_{n-1}, 1&#x2F;O_n(\boldsymbol{\theta}_0)),\text{ w&#x2F; } p_0=1
\end{aligned}$$&lt;&#x2F;p&gt;
&lt;p&gt;として定義すると&lt;&#x2F;p&gt;
&lt;p&gt;&lt;strong&gt;性質&lt;&#x2F;strong&gt;: $p_n\leq u$となる $n$ が存在する確率は $u$ 以下である ($0\leq u \leq 1$)&lt;&#x2F;p&gt;
&lt;p&gt;を持つことがわかる。&lt;&#x2F;p&gt;
&lt;p&gt;確かにこれは直感的に「普通の」p-value の拡張と思える。もし、ある時刻 $n$ で $p_n &amp;gt; u$ になったとすれば、それは帰無仮説の下では確率 $u$ 以下でしか起こらない稀な事象が起こったということであり、検定の文脈では背理法的に帰無仮説を棄却することになる。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;berunuiguo-cheng-nadohenoying-yong&quot;&gt;ベルヌーイ過程などへの応用&lt;&#x2F;h2&gt;
&lt;p&gt;以上は多項分布を生成する過程 (1) や (2) の話なので、一見すると使い道は限られるが、冒頭に述べた例のようにメトリクスをカウントデータとして読み替えることができれば広く適用できる。&lt;&#x2F;p&gt;
&lt;p&gt;もう少し具体的に、はじめに述べたコンバージョン有無を集計する AB テストを考えよう。$\boldsymbol{x}_n$ を $n$ 番目のコンバージョンがどの群で発生したかを表すものとする。例えば $\boldsymbol{x}_3 = (0, 1, 0)$ であれば3番目のコンバージョンが群2で発生したということである。群 $i$ におけるコンバージョン確率を $p_i$, また各ユーザーの群 $i$ への割り当て確率を $\rho_i$ とすると、次のコンバージョンがどの群で起こるかを表す確率は&lt;&#x2F;p&gt;
&lt;p&gt;$$\begin{aligned}
\theta_i = \frac{\rho_i p_i}{\sum_{j=1}^d \rho_i p_i}
\end{aligned}$$
とかける。帰無仮説は群ごとのコンバージョン確率 $p_i$ が全て等しいとするので、結局 $\boldsymbol\theta_0=\boldsymbol\rho$ として、上の話を適用すれば良いことになる。ポアソン過程についても同様に考えられるので、冒頭の論文を読んでほしい。&lt;&#x2F;p&gt;
&lt;p&gt;ちなみにこの表式から、$p_i$ が群の割り当てと独立な時間依存性を持っていても問題なく適用できることがわかる。Netflix の実験では、コンバージョン確率が実験中に時間変化することがあると報告されている。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;simiyuresiyon&quot;&gt;シミュレーション&lt;&#x2F;h2&gt;
&lt;p&gt;ベルヌーイ過程をシミュレーションして上記の手法を適用してみよう。実験に使ったノートブックは以下に上げた。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;yng87&#x2F;e400e5d871d23344a21789edee780d60&quot;&gt;sequential_ab_testing.ipynb&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Sequential p-value の計算はシンプルで、各時刻のカウントデータに対して式 (3) で $\boldsymbol\theta_0=\boldsymbol\rho$ としたものを計算していくだけである。ただし prior のパラメータ $\boldsymbol{\alpha}_0$ には自由度がある。論文では、帰無仮説からズレるとしてもそのズレは小さいだろうとして、$\boldsymbol{\alpha}_0 = k \boldsymbol{\theta}_0$ を採用している ($k$ は大きい正の数）。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#dirichlet&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Python で書くと以下のようになる &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#official-code&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; scipy&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;special&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; loggamma&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; xlogy&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; numpy&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; as&lt;&#x2F;span&gt;&lt;span&gt; np&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; sequential_p_value&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;counts&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; assignment_probabilities&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; k&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;100&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    counts&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; np&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;array&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;counts&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    assignment_probabilities&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; np&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;array&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;assignment_probabilities&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    dirichlet_alpha&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; k&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; assignment_probabilities&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    log_bf&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        loggamma&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;dirichlet_alpha&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; counts&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; -&lt;&#x2F;span&gt;&lt;span&gt; loggamma&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;dirichlet_alpha&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; counts&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        -&lt;&#x2F;span&gt;&lt;span&gt; loggamma&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;dirichlet_alpha&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; loggamma&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;dirichlet_alpha&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        -&lt;&#x2F;span&gt;&lt;span&gt; np&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;sum&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;xlogy&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;counts&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; assignment_probabilities&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; min&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; np&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;exp&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt;log_bf&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;ベータ関数はガンマ関数を使ってかけるのと、値が大きくなるのでできるだけ対数で計算しておくことがポイント。&lt;&#x2F;p&gt;
&lt;p&gt;二群の AA テスト ($\boldsymbol{p} = (0.5. 0.5)$) をして p-value の分布を見てみる。各群に割り当てられる確率は同じ ($\boldsymbol\rho=(0.5,0.5)$) とする。$n=4000$ まで取るシミュレーションを1000回繰り返し、上記の sequential p-value とカイ二乗検定を逐次行った結果を比較する。カイ二乗検定は各時刻で p-value を計算して最小値を保持するという、わざと不適切なやり方で行なった。&lt;&#x2F;p&gt;
&lt;p&gt;以下はその分布である。カイ二乗検定は明らかに p-value が0に寄ってしまっているのに対し、sequential p-value は概ね理想である一様に近い（少し1に偏っているのが気になるが…）。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;blog&#x2F;2024&#x2F;aa_test.png&quot; alt=&quot;AA テストでの sequential p-value とカイ二乗検定の p-value 分布&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;両者の Type I error の割合を有意水準 $0.05$ で計算してみると&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;カイ二乗検定: 0.032&lt;&#x2F;li&gt;
&lt;li&gt;Sequential p-value: 0.41&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;となり、カイ二乗検定は明らかに多重検定で Type I error が増幅されているのに対し、sequential p-value では適切に制御されていることがわかる。Sequential p-value の $p_n \leq u$ という性質はあくまで不等式なので、ピッタリ0.05 にはなっていないようだ。&lt;&#x2F;p&gt;
&lt;p&gt;上記の結果だけみると非常に便利で普通の検定より便利なのではと思ってしまうが、多重検定に対して頑健ということは、逆に検出力は低いのではないかという懸念が生じる。そこで $\boldsymbol{p} = (0.54. 0.5)$ と二群に差があるケースで同様にシミュレーションしてみた。ただし今回は多重検定にならないよう、カイ二乗検定では $n=4000$ で一回だけ p-value 計算をしている。この時、有意 ($p &amp;lt; 0.05$) になったものの割合は&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;カイ二乗検定: 0.715&lt;&#x2F;li&gt;
&lt;li&gt;Sequential p-value: 0.202&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;となった。やはり、検出力は低くなる傾向にあるようだ。&lt;&#x2F;p&gt;
&lt;p&gt;その他、実務の AB テストではゼロイチの統計量ではなく連続値に対して t 検定を行うこともよくあるが、その場合には使えないし、カウントに対するモデリングになっているので単純な拡張も難しそうだと思う。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;non-conversion&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;この場合、コンバージョンしなかったユーザーの数も必要でここではそれも取得できていると想定している。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;prior&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;Prior odds は1とする。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;dirichlet&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;この時 Dirichlet 分布の期待値は $\boldsymbol\theta_{0}$、分散は $\sim 1&#x2F;k$ である。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;official-code&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;4&lt;&#x2F;sup&gt;
&lt;p&gt;著者が &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;michaellindon&#x2F;5ce04c744d20755c3f653fbb58c2f4dd&quot;&gt;gist&lt;&#x2F;a&gt; にコードを上げているが、少し冗長な形になっている。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>トリガーを設定してABテストの感度を上げる</title>
		<published>2024-02-08T00:00:00+00:00</published>
		<updated>2024-02-08T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2024/abtest-trigger/"/>
		<id>https://yng87.page/blog/2024/abtest-trigger/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2024/abtest-trigger/">&lt;p&gt;AB テスト実践ガイドには AB テストの感度を上げるためのトリガーの設定（AB テスト対象者の定義の仕方）について書かれている。&lt;&#x2F;p&gt;

  



  



  



  



  



  



  



&lt;div class=&quot;embed-card embed-card--book&quot;&gt;
  &lt;a class=&quot;embed-card__link&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;www.kadokawa.co.jp&amp;#x2F;product&amp;#x2F;302101000901&amp;#x2F;&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;
    &lt;div class=&quot;embed-card__media&quot;&gt;
      
      &lt;img class=&quot;embed-card__image no-lightense&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;cdn.kdkw.jp&amp;#x2F;cover_1000&amp;#x2F;302101&amp;#x2F;302101000901.webp&quot; alt=&quot;A&amp;#x2F;B テスト実践ガイド真のデータドリブンへ至る信用できる実験とは&quot; loading=&quot;lazy&quot;&gt;
      
    &lt;&#x2F;div&gt;

    &lt;div class=&quot;embed-card__content&quot;&gt;
      
      &lt;div class=&quot;embed-card__label&quot;&gt;Book&lt;&#x2F;div&gt;
      

      &lt;div class=&quot;embed-card__title&quot;&gt;A&amp;#x2F;B テスト実践ガイド真のデータドリブンへ至る信用できる実験とは&lt;&#x2F;div&gt;

      
      &lt;div class=&quot;embed-card__meta&quot;&gt;Ron Kohavi &amp;#x2F; Diane Tang &amp;#x2F; Ya Xu &amp;#x2F; 大杉直也訳 &amp;#x2F; KADOKAWA&lt;&#x2F;div&gt;
      

      

      
      &lt;div class=&quot;embed-card__note&quot;&gt;2021-03-23発売 &amp;#x2F; ISBN 9784048930796&lt;&#x2F;div&gt;
      
    &lt;&#x2F;div&gt;
  &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;

&lt;p&gt;例えば EC サイトの商品ページで AB テストをするとしよう。商品ページ内の購入導線の変更や、そこに表示される関連アイテム推薦アルゴリズムの変更をテストしたいというような場合である。この時 AB テストの分析時に対象者として含めるユーザーの定義には次のようなものが考えられる&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;サイト訪問者全体&lt;&#x2F;li&gt;
&lt;li&gt;サイト訪問者全体のうち、商品ページを一度でも閲覧したユーザー&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;単に AB テストをしてデータを集めてメトリクスについて検定するだけであればどちらでも良い。しかしより感度の良い実験、つまり同じ人数でより小さいメトリクスの差まで検出できるような AB テストをしたいのであれば、後者を選択すべきである、と AB テスト実践ガイドには書かれている。&lt;&#x2F;p&gt;
&lt;p&gt;これは直感的に納得いくものだと思う。というのも、サイト訪問者のうち、商品ページに辿り着かなかったユーザーは、対象としている変更を一度も目にすることがないので、純粋なノイズになるからだ。これ以上の説明が不要なのであれば、以下を読む必要はない。&lt;&#x2F;p&gt;
&lt;p&gt;今回はこの事実をあえて数式を使って考えてみたいと思う。これは単にその方が自分が納得しやすいからだ。&lt;&#x2F;p&gt;
&lt;p&gt;上の問題を定式化しよう。まず以下のように変数を用意する&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;$y$ : テスト対象のメトリック。例えば、ユーザー一人当たりの商品購入金額など。&lt;&#x2F;li&gt;
&lt;li&gt;$z$ : ユーザーがテスト対象の変更があったページを閲覧したかどうかを表す二値変数。例えば、商品ページの訪問有無。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;すると $y$ の分布は
$$\begin{aligned}
p(y)
&amp;amp;= \sum_{z=0,1}p(y|z)p(z) \notag \\
&amp;amp;= p_zp(y|z=1) + (1-p_z)p(y|z=0)
\end{aligned}$$
とかける。ここで $z$ はベルヌーイ分布に従うとし、$z=1$ の確率を $p_z$ とした。また問題設定から、$p(y|z=0)$ は確率1で $y=0$ となる分布である。&lt;&#x2F;p&gt;
&lt;p&gt;この分布の平均値と分散を考えよう。まず条件付き分布の平均と分散を
$$\begin{aligned}
\mu_{y|z} &amp;amp;\equiv \mathbb{E}[Y|Z=1]\\
\sigma^2_{y|z} &amp;amp;\equiv \mathrm{Var}[Y|Z=1]
\end{aligned}$$
と定義すると、少し計算することで周辺分布での平均は
$$\begin{aligned}
\mu_{y}
\equiv \mathbb{E}[Y]
= p_z \mu_{y|z}
\end{aligned}$$
分散は
$$\begin{aligned}
\sigma_{y}^2
\equiv \mathrm{Var}[Y]
= p_z \sigma_{y|z}^2 + p_z(1-p_z)\mu_{y|z}^2
\end{aligned}$$
となることがわかる。&lt;&#x2F;p&gt;
&lt;p&gt;AB テストの感度は、サンプルサイズ $n$ を固定した時どこまで小さいメトリクスの差まで検出できるかで定式化できる。有意水準5%・検出力80%の場合には&lt;a href=&quot;https:&#x2F;&#x2F;yng87.page&#x2F;blog&#x2F;2021&#x2F;t-test-sample-size&#x2F;&quot;&gt;以前記事にもした&lt;&#x2F;a&gt;ように、以下の式で近似できる&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#hyp&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;
$$\begin{aligned}
\delta^2 = \frac{16\sigma^2}{n}
\end{aligned}$$&lt;&#x2F;p&gt;
&lt;p&gt;まずトリガーを設定せず、サイト訪問者全員をテスト対象者とした場合
$$\begin{aligned}
\delta_y^2 = 16\frac{p_z \sigma_{y|z}^2 + p_z(1-p_z)\mu_{y|z}^2}{n}
\end{aligned}$$
となる。トリガーを設定し、テスト対象のページの訪問者のみを考慮する場合は、サンプルサイズを $p_z\times n$ とすれば良いので
$$\begin{aligned}
\delta_{y|z}^2 = 16\frac{\sigma_{y|z}^2}{p_z n}
\end{aligned}$$
となる。あとは両者の比を取れば良いのだが、$\delta_{y|z}$ はそもそもトリガーされてないユーザー（例えば商品ページに辿り着かなかったユーザー ＝ 必ず $y=0$ となるユーザー）を考慮していないので、$\delta_y$ と比較するには $p_z$ をかける必要がある。ということで今見たい量は
$$\begin{aligned}
\frac{\delta_y^2}{p_z^2\delta_{y|z}^2}
=1 + (1-p_z)\left(\frac{\mu_{y|z}}{\sigma_{y|z}}\right)^2
\end{aligned}$$
である。これは（$p_z&amp;lt;1$ なら）1より大きいので $p_z\delta_{y|z} &amp;lt; \delta_y$ である。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#mu&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; つまり一般的に、&lt;strong&gt;トリガーを設定した方がより小さいメトリクスの差まで検出できるようになる&lt;&#x2F;strong&gt;ということを示すことができた。&lt;&#x2F;p&gt;
&lt;p&gt;またこの式から平均が分散に比べて大きいほど、トリガーの効果が大きく、逆の場合はトリガーの影響は軽微であることがわかる。これは分布がどれだけ $y=0$ 付近に密度を持っているかどうかを反映している。&lt;&#x2F;p&gt;
&lt;p&gt;まあ当たり前の関係なのだが、算数で明確に示すとすっきりする。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;hyp&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;この式が成立するには $n$ が十分大きく正規分布で諸々が近似できるというような状況が必要だが、ここでは典型的な Web での AB テストを考えているのでこの仮定は満たされているとして進める。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;mu&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;$\mu_{y|z}$ はコンバージョン率や購入金額など、正の量だと仮定している。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>ランキングモデルのポジションバイアスに対処する</title>
		<published>2024-01-13T00:00:00+00:00</published>
		<updated>2024-01-13T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2024/impl-click-modeling/"/>
		<id>https://yng87.page/blog/2024/impl-click-modeling/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2024/impl-click-modeling/">&lt;h2 id=&quot;hazimeni&quot;&gt;はじめに&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a href=&quot;https:&#x2F;&#x2F;yng87.page&#x2F;blog&#x2F;2023&#x2F;kdd2023-papers-3&#x2F;&quot;&gt;最近KDD2023の論文を読んでいた&lt;&#x2F;a&gt;のですが、その中で以下の論文が目に留まりました。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2212.13937&quot;&gt;Towards Disentangling Relevance and Bias in Unbiased Learning to Rank&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;Learning to rank におけるポジションバイアスに対処する手法の提案で、特に著者達が click modeling と呼ぶアプローチを発展させたものになっています。&lt;&#x2F;p&gt;
&lt;p&gt;今回はこの論文をベースにいくつかポジションバイアス除去系のモデルを実装して比較してみました。&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;
&lt;h2 id=&quot;pozisiyonbaiasunochu-qu&quot;&gt;ポジションバイアスの除去&lt;&#x2F;h2&gt;
&lt;p&gt;推薦や検索ではユーザーがサービスを使う間に自然に発生するクリックなどのイベントを、機械学習のラベルとして用いることがよくあります。しかしこのようなイベントの発生は、単純なユーザーの嗜好だけではなく、そもそもサービス上でユーザーがそのアイテムを目にする機会があったかどうかにも左右されます。嗜好が同程度のアイテムであれば上位に表示された方がクリックされやすくなるため、ポジションの影響を考慮に入れないとモデルの推論結果にバイアスが乗ってしまうことになります。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;baiasuchu-qu-noer-tunofang-fa&quot;&gt;バイアス除去の二つの方法&lt;&#x2F;h3&gt;
&lt;p&gt;ランキング学習におけるポジションバイアスの除去はここ数年ホットな話題となっており、多くの論文が書かれているようです。冒頭の論文ではこれまで提案されてきた手法を大きく以下の二つに分類していました&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Counterfactual アプローチ&lt;&#x2F;strong&gt;: 傾向スコアによる重み付けを使うなど、counterfactual な分析手法の応用&lt;&#x2F;li&gt;
&lt;li&gt;&lt;strong&gt;Click modeling アプローチ&lt;&#x2F;strong&gt;: アイテムのクリック確率を、アイテムの観測確率と、観測された上でクリックされる確率（CTR）に分解しモデリングする方法&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Counterfactual アプローチでは傾向スコアを別途何らかの方法で入手・推定する必要があり、手間やコストがかかります。一方 click modeing アプローチは、サービスのログデータを使って通常のモデリングプロセスの枠内で実装できます。そのような手のつけやすさから産業界ではこちらがよく採用されるらしく、実際例えば &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2002.05515&quot;&gt;Airbnb は click modeling アプローチを採用して実装したモデルの有効性を報告しています&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;click-modeling-noaideia&quot;&gt;Click modeling のアイディア&lt;&#x2F;h3&gt;
&lt;p&gt;Click modeling のアイディアはシンプルです。推薦や検索でユーザーにアイテムのリストを表示するような状況を念頭に起きます。ユーザー・アイテムペアを表す識別子を $i$ として変数を以下のように定義します。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;$c_i$: ユーザーがアイテムをクリックしたかを表す二値変数&lt;&#x2F;li&gt;
&lt;li&gt;$o_i$: ユーザーがアイテムを観測したかを表す二値変数&lt;&#x2F;li&gt;
&lt;li&gt;$\vec x_i$: ユーザー・アイテムの特徴量&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;一般にクリック確率は
$$\begin{aligned}
\tag{1}
P(c_i=1|\vec x_i) = P(c_i=1|\vec x_i, o_i=1)\times P(o_i=1|\vec x_i)
\end{aligned}$$&lt;&#x2F;p&gt;
&lt;p&gt;と分解できます。ここで、$o_i=0$ ならば必ず $c_i=0$ になるとしました。&lt;&#x2F;p&gt;
&lt;p&gt;さらに特徴量 $\vec x_i$ が、以下のように分離できると仮定しましょう&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;$\vec x_i^R$: ユーザーのアイテムに対する嗜好 (以下 relevance とも言います) のみに関係する特徴量。&lt;&#x2F;li&gt;
&lt;li&gt;$\vec x_i^P$: アイテムの観測有無にのみ関係する特徴量。例えば、表示位置やデバイス情報。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;つまり、式 (1) が以下のように書けるとします
$$\begin{aligned}
\tag{2}
P(c_i=1|\vec x_i) = P(c_i=1|\vec x_i^R, o_i=1)\times P(o_i=1|\vec x_i^P)
\end{aligned}$$&lt;&#x2F;p&gt;
&lt;p&gt;このようにクリック確率を、アイテムの観測確率と観測されたという条件付きでのクリック確率の積として表し、それぞれを異なる特徴量を持つ別のモデルとして表現するのが click modeling アプローチです。ユーザーの嗜好は第一項の $P(c_i=1|\vec x_i^R, o_i=1)$ で表されており、推論時にはこちらだけを使います。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pal&quot;&gt;PAL&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;dl.acm.org&#x2F;doi&#x2F;10.1145&#x2F;3298689.3347033&quot;&gt;PAL&lt;&#x2F;a&gt; は click modeling アプローチの走りで、$P(c_i=1|\vec x_i^R, o_i=1)$ と $P(o_i=1|\vec x_i^P)$ をそれぞれニューラルネットワーク $r(\vec x_i^R)$ と $o(\vec x_i^P)$ で表し、以下のようなロス関数を使って学習することを提案しました
$$\begin{aligned}
\tag{3}
L &amp;amp;= \frac{1}{N}\sum_{i=1}^N l(c_i, \psi_i) \\
\psi_i &amp;amp;= r(\vec x_i^R)o(\vec x_i^P)\notag
\end{aligned}$$
ここで $l$ はクロスエントロピーです。推論時には $r(\vec x_i^R)$ だけを用いることで、ポジションの影響を取り除いたユーザーの嗜好を予測することができます。&lt;&#x2F;p&gt;
&lt;p&gt;式 (2) を見ると上記の積の形が自然に思えますが、冒頭の論文で引用されている様子を見ると $\psi_i=\mathrm{sigmoid}(r(\vec x_i^R)+o(\vec x_i^P))$ という和の形を採用することもあるようです。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#additive&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;また冒頭の論文ではこのようなモデルを two tower model、 $r(\vec x_i^R)$ を relevance tower, $o(\vec x_i^P)$ を observation tower と呼んでいます。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#rec-two-tower&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;relevance-niyorujiao-luo&quot;&gt;Relevance による交絡&lt;&#x2F;h3&gt;
&lt;p&gt;PAL はポジションの影響を $o(\vec x_i^P)$ だけに押し付けるという発想ですが、式 (3) のように単に特徴量を分割して積や和の形にモデルを書いただけでそう都合よくバイアスが緩和できるものなのでしょうか。&lt;&#x2F;p&gt;
&lt;p&gt;冒頭の論文では、PAL のような手法がうまくいくのはデータの観測構造が下図 (a) のようになっている時だと指摘しました（冒頭の論文より。$D$ が $\vec x^R$, $B$ が $o$, $P$ が $\vec x^P$ と思ってください）。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;blog&#x2F;2024&#x2F;ultr_dag.png&quot; alt=&quot;Unbiased learning to rank の観測構造を表す DAG&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;観測有無はポジションによってのみ決まり、relevance の影響は受けないという仮定になっています。&lt;&#x2F;p&gt;
&lt;p&gt;しかし実務では多くの場合、ログデータは過去のモデルの推論結果から生成されており、そのモデルはやはり relevance を学習しているため、(b) のように relevance がポジションとクリック両方の原因（交絡因子）になります。するとそのようなデータで学習したモデルは、relevance の学習のためにポジションを &lt;strong&gt;shortcut&lt;&#x2F;strong&gt; として利用できるようになってしまい、relevance tower にポジションの影響が入り込んでバイアスの除去を妨げてしまいます。&lt;&#x2F;p&gt;
&lt;p&gt;上記論文ではこの shortcut を緩和する手法として以下二つを提案していました。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;observation-dropout&quot;&gt;Observation Dropout&lt;&#x2F;h4&gt;
&lt;p&gt;これはその名の通り、observation tower $o(\vec x^P)$ に dropout を導入して、ポジションの shortcut を緩和します。ロス関数はこれまでと同じ式 (3) です。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;gradient-reversal&quot;&gt;Gradient Reversal&lt;&#x2F;h4&gt;
&lt;p&gt;この方法では、observation tower $o(\vec x^P)$ から relevance を &lt;strong&gt;unlearn&lt;&#x2F;strong&gt; します。具体的には式 (3) に以下のロス関数を加えて joint で学習します。
$$\begin{aligned}
L_{rev}=\sum_i\left(y_i - \mathrm{Dense}(\mathrm{GradRev}(o(\vec x^P_i)))\right)^2
\end{aligned}$$
ここで $\mathrm{GradRev}$ は勾配の符号を反転させるレイヤーです (forward では何もしません）。$y_i$ は relevance のラベルで、ユーザーの真の嗜好を表しますが、通常は利用可能でないのでクリックラベルで代用したり、$r(\vec x^R)$ を使ったりすることが提案されています。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yan&quot;&gt;実験&lt;&#x2F;h2&gt;
&lt;p&gt;冒頭の論文の提案手法も含めて、click modeling アプローチの効果を確かめる実験を行いました。基本的には論文に記載の手順に従いましたが、詳細が書かれていない部分も多くその場合は適当に想像して補っています。実装は以下に置いておきました。&lt;&#x2F;p&gt;



  



  



  


&lt;div class=&quot;github-card&quot;&gt;
  &lt;a class=&quot;github-card__link&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;yng87&amp;#x2F;unbiased-ltr&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;
    &lt;div class=&quot;github-card__badge&quot; aria-hidden=&quot;true&quot;&gt;
      &lt;svg xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;20&quot; height=&quot;20&quot;&gt;&lt;path d=&quot;M5.88401 18.6531C5.58404 18.4523 5.32587 18.1972 5.0239 17.8366C4.91473 17.7062 4.47283 17.1521 4.55811 17.258C4.09533 16.6831 3.80296 16.4168 3.50156 16.3087C2.9817 16.1223 2.7114 15.5497 2.89784 15.0298C3.08428 14.51 3.65685 14.2397 4.17672 14.4261C4.92936 14.696 5.43847 15.1609 6.12425 16.0141C6.03025 15.8972 6.46364 16.4408 6.55731 16.5526C6.74784 16.7802 6.88732 16.918 6.99629 16.9909C7.20118 17.128 7.58451 17.1871 8.14709 17.1308C8.17065 16.7487 8.24136 16.378 8.34919 16.0355C5.38097 15.3101 3.70116 13.3949 3.70116 9.63947C3.70116 8.4006 4.0704 7.28368 4.75917 6.34756C4.5415 5.45368 4.57433 4.37259 5.06092 3.15611C5.1725 2.87715 5.40361 2.66314 5.69031 2.57328C5.77242 2.54949 5.81791 2.5389 5.89878 2.52648C6.70167 2.40319 7.83573 2.69681 9.31449 3.62311C10.181 3.41855 11.0885 3.31476 12.0012 3.31476C12.9129 3.31476 13.8196 3.41835 14.6854 3.62253C16.1619 2.68976 17.2986 2.39625 18.1072 2.52627C18.1919 2.53988 18.2645 2.55758 18.3249 2.57741C18.6059 2.66967 18.8316 2.88155 18.9414 3.15611C19.4279 4.37232 19.4608 5.45319 19.2433 6.34695C19.9342 7.28313 20.3012 8.39184 20.3012 9.63947C20.3012 13.3966 18.627 15.3046 15.6588 16.0318C15.7837 16.4467 15.8496 16.9103 15.8496 17.4118C15.8496 18.0763 15.8471 18.7108 15.8424 19.4223C15.8412 19.6124 15.8397 19.8156 15.8375 20.1279C16.2129 20.2107 16.5229 20.5074 16.6031 20.9086C16.7114 21.4502 16.3602 21.977 15.8186 22.0853C14.6794 22.3132 13.8353 21.5535 13.8353 20.5608C13.8353 20.4705 13.836 20.3414 13.8375 20.1142C13.8398 19.8012 13.8412 19.5987 13.8425 19.4092C13.8471 18.7017 13.8496 18.0714 13.8496 17.4118C13.8496 16.7145 13.6664 16.26 13.4237 16.0508C12.7627 15.481 13.0977 14.3971 13.965 14.2996C16.9314 13.9663 18.3012 12.8174 18.3012 9.63947C18.3012 8.68484 17.9893 7.89547 17.3881 7.23534C17.1301 6.95209 17.0567 6.54634 17.199 6.19062C17.3647 5.77639 17.4354 5.2336 17.2941 4.57678L17.2847 4.57944C16.7928 4.71861 16.1744 5.01956 15.4261 5.52826C15.182 5.69413 14.8772 5.74377 14.5932 5.66388C13.7729 5.43319 12.8913 5.31476 12.0012 5.31476C11.111 5.31476 10.2294 5.43319 9.40916 5.66388C9.12662 5.74335 8.82344 5.69468 8.57997 5.53077C7.8274 5.02414 7.2056 4.72355 6.71079 4.58352C6.56735 5.23672 6.63814 5.77758 6.80336 6.19062C6.94565 6.54634 6.87219 6.95209 6.61423 7.23534C6.01715 7.89096 5.70116 8.69352 5.70116 9.63947C5.70116 12.8114 7.07225 13.9681 10.023 14.2996C10.8883 14.3969 11.2246 15.4767 10.5675 16.048C10.3751 16.2153 10.1384 16.7799 10.1384 17.4118V20.5608C10.1384 21.5472 9.30356 22.2866 8.17878 22.0898C7.63476 21.9946 7.27093 21.4764 7.36613 20.9324C7.43827 20.5201 7.75331 20.2114 8.13841 20.1274V19.1379C7.22829 19.1991 6.47656 19.0496 5.88401 18.6531Z&quot; fill=&quot;currentColor&quot;&gt;&lt;&#x2F;path&gt;&lt;&#x2F;svg&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;github-card__content&quot;&gt;
      &lt;div class=&quot;github-card__repo&quot;&gt;yng87&amp;#x2F;unbiased-ltr&lt;&#x2F;div&gt;
      
      &lt;div class=&quot;github-card__description&quot;&gt;Reproduction scripts for unbiased learning-to-rank methods&lt;&#x2F;div&gt;
      
      
      &lt;div class=&quot;github-card__meta&quot;&gt;Python&lt;&#x2F;div&gt;
      
    &lt;&#x2F;div&gt;
  &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;h3 id=&quot;kuritukudetasetutonosheng-cheng&quot;&gt;クリックデータセットの生成&lt;&#x2F;h3&gt;
&lt;p&gt;バイアス除去に関する実験をするには、バイアスの乗った学習データと、バイアスのないテストデータが必要になります。ここでは論文にならって、&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.microsoft.com&#x2F;en-us&#x2F;research&#x2F;project&#x2F;mslr&#x2F;&quot;&gt;MSLR-WEB30K&lt;&#x2F;a&gt; を加工してそのようなデータセットを作ることにします。MSLR-WEB30K は Bing の検索から作った learning-to-rank 用のデータセットで、各クエリ・ドキュメントのペアに対して、136次元の特徴量ベクトルと、0-4 の relevance ラベル $y_i$ が付けられています。この $y_i$ をバイアスのない ground truth ラベルとします。&lt;&#x2F;p&gt;
&lt;p&gt;このデータセットにクリックの有無を表す二値ラベルを、以下の手順で人工的に付与します。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;1-kuerigotonidokiyumentonopozisiyonwosheng-cheng&quot;&gt;1. クエリごとにドキュメントのポジションを生成&lt;&#x2F;h4&gt;
&lt;p&gt;各ドキュメントに以下で表されるスコア $s_i$ を生成し、クエリごとにその降順をドキュメントのポジション $p_i$ とします。
$$\begin{aligned}
s_i = wy_i + (1 - w)n_i,\text{  where } n_i \sim \mathrm{Uniform(0,4)}
\end{aligned}$$&lt;&#x2F;p&gt;
&lt;p&gt;スコアは relevance $y_i$ とランダムノイズの重み付き和で決まります。$w=1$ の時は完全に relevance によって決まり上で述べた交絡が強く発生しているという状況になります。逆に $w=0$ の時はポジションは relevance によらずランダムに決まり、交絡はありません。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;2-pozisiyonniying-zitaren-gong-kuritukunosheng-cheng&quot;&gt;2. ポジションに応じた人工クリックの生成&lt;&#x2F;h4&gt;
&lt;p&gt;ポジションが上になるほど表示確率が高いという状況を
$$\begin{aligned}
P(o_i=1|p_i) = \frac{1}{p_i}
\end{aligned}$$&lt;&#x2F;p&gt;
&lt;p&gt;と表現することにします。そして、$o_i=1$ の下でのクリック確率を
$$\begin{aligned}
P(c_i=1|y_i, o_i=1)=\epsilon + (1-\epsilon)\frac{2^{y_i}-1}{2^{y_{\mathrm{max}}}-1}
\end{aligned}$$&lt;&#x2F;p&gt;
&lt;p&gt;とします。Relevance が高いほどクリックしやすく、また一定値のノイズ $\epsilon$ も付与されるという設定です。ここでは元論文にならい $\epsilon=0.1$ に固定します。&lt;&#x2F;p&gt;
&lt;p&gt;トータルのクリック確率はこの二つの積&lt;&#x2F;p&gt;
&lt;p&gt;$$
P(c_i=1|y_i,p_i)=P(c_i=1|y_i, o_i=1)\times P(o_i=1|p_i)
$$&lt;&#x2F;p&gt;
&lt;p&gt;になります。MSLR-WEB30K の各データに対して $P(c_i=1|y_i,p_i)$ に基づくベルヌーイ分布からサンプリングを行いました。&lt;&#x2F;p&gt;
&lt;p&gt;なお、この方法でクリックを生成すると正例 ($c_i=1$) が1%以下と不均衡が大きくなるため、以下の実験では負例を10%にダウンサンプリングして学習の効率化を図っています。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;bi-jiao-yong-nobesurain&quot;&gt;比較用のベースライン&lt;&#x2F;h3&gt;
&lt;p&gt;比較のために、以下のベースラインを用意しました。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;linear-model&quot;&gt;Linear model&lt;&#x2F;h4&gt;
&lt;p&gt;目的変数を $c_i$ とする二値分類を、正則化項つきのロジスティック回帰で解きます。実装には scikit-learn を用いました。&lt;&#x2F;p&gt;
&lt;p&gt;またポジションバイアスを除去するため、学習時にポジションも特徴量として使い、推論時には一定値 ($p_i=1$) に固定するという方法も試しました (Debiased Linear)。これは&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;developers.google.com&#x2F;machine-learning&#x2F;guides&#x2F;rules-of-ml#rule_36_avoid_feedback_loops_with_positional_features&quot;&gt;GoogleのRules of machine learning&lt;&#x2F;a&gt;にも紹介されている古典的なテクニックです。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;gbdt&quot;&gt;GBDT&lt;&#x2F;h4&gt;
&lt;p&gt;XGBoost の &lt;code&gt;objective=&quot;rank:ndcg&quot;&lt;&#x2F;code&gt; でランキングモデルを作ります。またロジスティック回帰と同様に、ポジション特徴量を推論時に固定する方法も試しました（Debiased XGBoost）。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#xgboost&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;h4 id=&quot;single-tower&quot;&gt;Single Tower&lt;&#x2F;h4&gt;
&lt;p&gt;目的変数を $c_i$ とする二値分類を MLP で解くモデルです。公平な比較をするため、two tower 系のモデルの relevance tower のパラメータ数は、この single tower と等しくなるようにしています。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;validation-nofang-fa&quot;&gt;Validation の方法&lt;&#x2F;h3&gt;
&lt;p&gt;モデルのハイパーパラメータチューニングや early stopping などは、生成した人工クリックに基づく validation データを通して行いました。このクリックは本来最適化したい relevance に対してバイアスがあるわけですが、現実的な設定にしようと思うと多少バイアスが乗っていてもこれを使うしかありません（冒頭の論文にはこの辺の手法の詳細は書かれていませんでした）。&lt;&#x2F;p&gt;
&lt;p&gt;なお手元の計算リソースの都合上、パラメータチューニングといっても適当に手動で何パターンか試して良さそうなものに固定する、という程度のことしかしていません。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-yan-jie-guo&quot;&gt;実験結果&lt;&#x2F;h2&gt;
&lt;p&gt;以下に test データの relevance ラベルにおける、NDCG@5 を示します。バイアスのある人工クリックと異なり、これは真に評価したい unbiased な指標になります。&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th style=&quot;text-align: left&quot;&gt;w&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;linear&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;debiased_linear&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;xgboost&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;debiased_xgboost&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;single_tower&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;two_tower&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;obs_dropout&lt;&#x2F;th&gt;&lt;th style=&quot;text-align: right&quot;&gt;grad_rev&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;0&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2787&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.3145&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2683&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.1828&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2658&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;0.3465&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2860&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.3382&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;0.2&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2851&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.3157&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2964&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.1737&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2983&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;0.3437&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2984&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.3432&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;0.6&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2935&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.3227&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.3057&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2426&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2436&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.3255&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;0.3434&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.3341&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;0.8&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2957&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.3246&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.3124&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.1768&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2924&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;0.3532&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.3163&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.3439&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td style=&quot;text-align: left&quot;&gt;1&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2880&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.3189&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2927&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.1599&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.3075&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;&lt;strong&gt;0.3401&lt;&#x2F;strong&gt;&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.2976&lt;&#x2F;td&gt;&lt;td style=&quot;text-align: right&quot;&gt;0.3399&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;この結果から次のことがわかりました。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;ロジスティック回帰（Linear）は position 特徴量の利用で大きく指標が向上する
&lt;ul&gt;
&lt;li&gt;逆に XGBoost ではこの手法は有効ではない&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;ニューラルネットワークを使った手法では、バイアスを考慮しない single tower に比べて、バイアスを考慮したモデルが強い。&lt;&#x2F;li&gt;
&lt;li&gt;論文の主張であった、two tower モデル (PAL) は $w$ が大きい（relevance の交絡が強い）と性能が落ちるという点は再現できなかった。
&lt;ul&gt;
&lt;li&gt;そもそも他の結果も元論文とは数字がかなり異なる。論文では人工クリックの生成・モデルの実装・ハイパーパラメータについて何も共有されていないのでこの辺りは仕方ない（もちろん自分の実装にバグがある可能性もある）。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;なお、Debaised XGBoost は非常に悪い結果となりましたが、自分はこの方法が上手くいく場面を実務で見たことがあるので、データセットとの相性も大きいのではないかと思います。直感的には今回はポジションの影響が $1&#x2F;p_i$ の形で入ってくるのでほとんどポジションで学習できてしまい、relevance 特徴をうまく学習できていないのだと思います。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;saigoni&quot;&gt;さいごに&lt;&#x2F;h2&gt;
&lt;p&gt;次は counterfactual 系の実用的な手法を調べて比較してみたいです。実装にバグを見つけたら、リポジトリで報告をして頂けると助かります。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;additive&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;自分で実験したところ、和の形の方が少しだけ性能が良かったです。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;rec-two-tower&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;推薦の retrieval で使われる two-tower モデルとは異なります。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;xgboost&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;xgboost.readthedocs.io&#x2F;en&#x2F;latest&#x2F;tutorials&#x2F;learning_to_rank.html#position-bias&quot;&gt;XGBoostには最近ポジションバイアス除去のオプションが追加された&lt;&#x2F;a&gt;らしく、&lt;code&gt;lambdarank_unbiased=True&lt;&#x2F;code&gt; で使えるようですが、試してみたところあまり効果はなかったです。ちゃんと内容を理解していないので比較対象からは抜いています。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>KDD2023の読んだ論文まとめ (3)</title>
		<published>2023-12-05T00:00:00+00:00</published>
		<updated>2023-12-05T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2023/kdd2023-papers-3/"/>
		<id>https://yng87.page/blog/2023/kdd2023-papers-3/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2023/kdd2023-papers-3/">&lt;p&gt;&lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;kdd2023-papers-2&#x2F;&quot;&gt;前回&lt;&#x2F;a&gt;、&lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;kdd2023-papers-1&#x2F;&quot;&gt;前々回&lt;&#x2F;a&gt;の続きで、KDD2023の読んだ論文に対する感想を書いていく。
これで読みたいと思ってリストアップしたものは全部一通り目を通せた。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;transact-transformer-based-realtime-user-action-model-for-recommendation-at-pinterest&quot;&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2306.00248&quot;&gt;TransAct: Transformer-based Realtime User Action Model for Recommendation at Pinterest&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;毎度お馴染み Pinterest の推薦に関する論文。今回はランキングでユーザーの短期と長期の興味をそれぞれ別の形で取り込むモデルを紹介している。
本筋のモデル構造の話よりも、学習データの作り方として、ターゲットの時刻の直前 T 秒の系列は使わないという、time window mask アプローチが参考になった。
直前のアイテムの情報が使えないと、推薦に多様性が出るらしい。タスクとしても難易度が高くなるので良さそうな気がする。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;rankformer-listwise-learning-to-rank-using-listwide-labels&quot;&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2306.05808&quot;&gt;RankFormer: Listwise Learning-to-Rank Using Listwide Labels&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Amazon からの論文で、ランキングモデルの学習において普通は捨ててしまう、ユーザーのフィードバックが一つもつかなかった推薦リストも学習に使おうというアイディア。
Transformer ベースの neural ranking model を用い、アイテム系列に &lt;code&gt;[CLS]&lt;&#x2F;code&gt; トークンを付与してそれをリスト全体としてクリックがあったかどうかのラベル学習に用いる。
実際 Amazon の AB テストでも良い成績だったらしいが、そこでは実装の都合上直接このモデルを用いず、知識蒸留した GBDT を使ったとのこと。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;balancing-approach-for-causal-inference-at-scale&quot;&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2302.05549&quot;&gt;Balancing Approach for Causal Inference at Scale&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;因果推論の balancing approach のスケールするアルゴリズムを提案した論文。
そもそも balancing approach というのを知らなかったが、IPW などの単純な方法では共変量のバランスチェックをする必要があり、大規模なデータセットでは試行錯誤するのが大変らしい。
そこで propensity を使わず各ユニットの重みを、共変量バランスを満たすという制約のもとで直接推定する手法 (balancing approach) が採用されるとのこと。
この研究の貢献は、single machine でしか使えない既存手法を改善して、大規模な問題でもスケールする分散アルゴリズムを提案した点。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;towards-disentangling-relevance-and-bias-in-unbiased-learning-to-rank&quot;&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2212.13937&quot;&gt;Towards Disentangling Relevance and Bias in Unbiased Learning to Rank&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Google research の unbiased learning-to-rank (LtR) に関する論文。
Unbiased LtR には大きく二流派あり、IPS のようなものを使う counterfatual 的アプローチと、モデルを position bias を受ける部分と受けない部分の二つに分ける click modeling アプローチがある。
産業界では後者の click modeling アプローチが主流とのこと（知らなかった）。
Click modeling では、バイアスを受けづらい特徴量を使う relevance tower と、position やデバイス情報などのバイアスを受けやすい特徴量を使う observation tower の二つを用意する two-tower モデルがよく使われる。
しかし、実際のサービスでは position などはモデルの推論結果から生成される。そのモデルは以前の relevance を学習するので、実は交絡は解消できていないという点を指摘している。
これを解決するために二つのシンプルな方法を提案し、オンラインテストの結果も載せている。
KDD2023で一番勉強になった。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;optimizing-airbnb-search-journey-with-multi-task-learning&quot;&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2305.18431&quot;&gt;Optimizing Airbnb Search Journey with Multi-task Learning&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Airbnb の検索ランキングモデル改善論文。
サービスの特徴として、宿を予約するまでのユーザージャーニーが非常に長く（数週間のオーダー）、既存のモデルではその中で最終的に予約された宿に関するクリックシグナルしか使えていないという課題があったとのこと。
提案手法の中では、$P(uncancelled\ booking) = P(uncancelled\ booking|booking)\times P(booking)$ のようなビジネス指標の分解をそのままモデルとロスの構造に反映させている部分が面白かった。
ただオンライン実験結果はポジティブとはいえ、ここまでやった割に上昇幅は渋いなぁという感想。
まあプラットフォームが成熟しているとメトリクスの大幅な改善は難しいのだろう。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;multi-label-learning-to-rank-through-multi-objective-optimization&quot;&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2207.03060&quot;&gt;Multi-Label Learning to Rank through Multi-Objective Optimization&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;GBDT における multi-label LtR モデルの学習アルゴリズムのトレードオフを整理した論文。
Multi-objective 用のアルゴリズムはいろいろ提案されているらしいが、それを整理してパレートフロントのどの点に収束できてどの点にできないかを見るフレームワークを導入したのが貢献のよう。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;improving-training-stability-for-multitask-ranking-models-in-recommender-systems&quot;&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2302.09178&quot;&gt;Improving Training Stability for Multitask Ranking Models in Recommender Systems&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;YouTube で使われているランキングモデルの training stability 改善手法についての論文。
推薦では複数の目的関数を持つ DNN モデルが使われるが、学習が不安定で loss が発散することが多い。
学習の安定化のために、gradient clipping や warmup などのテクニックが開発されていて他の分野では成功しているが、multi-objective ランキングモデルには不十分な傾向がある。
この原因として、sequential training を行うためデータの分布変化が激しく loss が steep になりやすい、モデルサイズが大きい、multi-objective モデルは一つのタスクの不安定性が全体に伝播しやすいという点を挙げている。
提案手法は、AdaGrad の安定性を向上させるための新しい gradient clipping。
YouTube のデータを使った実験では良い成績とのことで、特に learning rate をそこまで小さくせずとも済むのが嬉しい模様。
あとはそもそも YouTube では複数の candidate generation と multi-objective を使ったランキングで構成されているという点が勉強になった。&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>KDD2023の読んだ論文まとめ (2)</title>
		<published>2023-11-26T00:00:00+00:00</published>
		<updated>2023-11-26T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2023/kdd2023-papers-2/"/>
		<id>https://yng87.page/blog/2023/kdd2023-papers-2/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2023/kdd2023-papers-2/">&lt;p&gt;&lt;a href=&quot;&#x2F;blog&#x2F;2023&#x2F;kdd2023-papers-1&#x2F;&quot;&gt;前回&lt;&#x2F;a&gt;の続きで、KDD2023の読んだ論文に対する感想をつらつら書いていく。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;multi-factor-sequential-re-ranking-with-perception-aware-diversification&quot;&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2305.12420&quot;&gt;Multi-factor Sequential Re-ranking with Perception-Aware Diversification&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Alibaba のリランキングにおける accuracy と diversity の両立に関する論文。ユーザーが上から順にアイテムフィードを見ていくような場面で、それまで見たアイテムを考慮して次に提示するアイテムを決めるような文脈を考えている。&lt;&#x2F;p&gt;
&lt;p&gt;既存の Determinantal point process (DPP) という手法をベースに、ユーザー $u$ に推薦するアイテムの集合 $S$ を、$h(u, S) = \sum_{i\in S}g(u,i|S) + \alpha \log\mathrm{det}(D^u_S)$ を最大化するものとして選ぶとしている。$g(u,i|S)$ がユーザー $u$ のアイテム $i$ に対する選好度で、$D^u_S$ が $S$ に含まれるアイテムの、ユーザー $u$ への推薦という文脈を考慮した類似度行列。類似度がユーザー依存性を持っているのがポイントで、多様性を考慮する重要度をユーザーに応じて変えられるのが良いらしい。こういう定式化があるのかと勉強になった。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;controllable-multi-objective-re-ranking-with-policy-hypernetworks&quot;&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2306.05118&quot;&gt;Controllable Multi-Objective Re-ranking with Policy Hypernetworks&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;これも Alibaba の論文で、multi-objective の各目的関数間の相対的な重みの変更に強いランキングアルゴリズムを作ろうという提案。セールや季節性などの影響で、時々重みを変えたくなるが、毎回それに適したモデルをスクラッチで学習させるのはコストが高いというのが背景にある。正しく読めているか怪しいが、大雑把にいうとモデルの最後の数層（MLP など）のパラメータを生成する hypernetwork を別に用意し、それ以外のパラメータは固定の上で、multi-objective の重みが変わったらこの数層だけを取り替えるというような提案をしている。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;joint-optimization-of-ranking-and-calibration-with-contextualized-hybrid-model&quot;&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2208.06164&quot;&gt;Joint Optimization of Ranking and Calibration with Contextualized Hybrid Model&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;ランキング能力の最適化と、calibration を同時に実現する方法の提案。通常二値分類の logit は一次元だが、これを二次元に拡張し (positive probability, negative probability) 両方に対して用意する。ロスが二値分類の cross entropy のみであれば、この差しか学習できいが、ranking loss を追加することで両方学習できるようにしている。理論的には energy based model とみなせるとも書いてあって面白かったが、実質的には普通の listwise loss に比べて負例同士の比較部分にしか差がなさそうで、そんなに既存手法と変わるのだろうかという疑問がある。&lt;&#x2F;p&gt;
&lt;p&gt;あとこれは自分が知識不足なだけだが、線形でない複雑なモデルの場合も cross entropy で学習させれば必ず calibration されるのだっけ。Negative downsampling とかをせずデータセットの正例・負例のバランスを壊さなければ良いのだろうか。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;fresh-content-needs-more-attention-multi-funnel-fresh-content-recommendation&quot;&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2306.01720&quot;&gt;Fresh Content Needs More Attention: Multi-funnel Fresh Content Recommendation&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Google のレコメンドに関する論文で、interaction の少ないアイテムの推薦を増やしてカバレッジを増やしつつ、エンゲージメントも落としたくないというのがモチベーション。通常の推薦枠の一つを fresh content 用の枠に置き換えて新規アイテムのデータを取得しているらしい。またこの枠の推薦では、アイテム ID などは使わずできるだけ汎化しやすい特徴量のみを利用し、fresh content のデータの溜まり具合に応じて使うモデルを変えたりしているらしい。なんとなく全体的に要領を得ないというか、ポイントが絞られていない論文だった。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;empowering-long-tail-item-recommendation-through-cross-decoupling-network-cdn&quot;&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2210.14309&quot;&gt;Empowering Long-tail Item Recommendation through Cross Decoupling Network (CDN)&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;これも Google の論文で、long-tail アイテムの推薦を改善する手法を提案している。普通の協調フィルタリング的手法では tail のアイテムの評価が悪く、かといって既存手法に倣って全データで学習したあと、tail のアイテム中心のデータで再学習するなどすると head のアイテムの性能が落ちる（最初の学習内容の忘却が起こる）。そこで、head アイテムの性能を維持しつつ tail でも良い性能を得るため、アイテム特徴量の分離と tail アイテムデータセットによる正則化を導入した。特徴量の分離というのは、アイテム ID のような暗記が中心の埋め込みと、カテゴリ ID のように複数アイテム間で汎化させることが目的の埋め込みを別に集約し、アイテムの frequency に応じたゲートに通して足し合わせるというもの。なかなか面白かった。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;counterfactual-video-recommendation-for-duration-debiasing&quot;&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;dl.acm.org&#x2F;doi&#x2F;abs&#x2F;10.1145&#x2F;3580305.3599797&quot;&gt;Counterfactual Video Recommendation for Duration Debiasing&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;ByteDance の推薦におけるバイアス除去に関する論文。動画推薦では、短い動画の方が視聴完了率が高い = positive feedback が得やすいため、推薦にも表示されやくすなってしまう（duration bias が強い）。そこで、動画を見終わったかどうかでなく、何割まで見たかという閾値を定義して二クラスに分けるようにしたとのこと。分け方は、クラス内分散を小さく、クラス間分散を大きくなるように決めている。また DAG に基づいた因果推論をして、total indirect effect を計算し推薦に用いている。&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>KDD2023の読んだ論文まとめ (1)</title>
		<published>2023-11-19T00:00:00+00:00</published>
		<updated>2023-11-19T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2023/kdd2023-papers-1/"/>
		<id>https://yng87.page/blog/2023/kdd2023-papers-1/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2023/kdd2023-papers-1/">&lt;p&gt;最近は KDD2023の Applied Data Science Track で気になった論文を少しずつ読んでいるので、簡単にメモを残そうと思う。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;variance-reduction-using-in-experiment-data-efficient-and-targeted-online-measurement-for-sparse-and-delayed-outcomes&quot;&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;dl.acm.org&#x2F;doi&#x2F;10.1145&#x2F;3580305.3599928&quot;&gt;Variance Reduction Using In-Experiment Data: Efficient and Targeted Online Measurement for Sparse and Delayed Outcomes&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;A&#x2F;B テストの感度を上げるために、実験中のデータも使ってメトリクスの分散を下げようという提案。Airbnb で実際に有効に機能しているらしい。この話題だと、CUPED のように実験前のデータを使って分散の小さいメトリクスを構成するアイディアがよく知られているが、実験中にライブでその値をアップデートしていくのが新しい。&lt;&#x2F;p&gt;
&lt;p&gt;CTR のような単純なメトリクスは分散が小さく実験で白黒付けやすいことが多い一方で、本当に上げたい LTV 的指標は検出力が低いという状況は、実際にサービスの改善に携わるとよく経験する。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;multi-channel-integrated-recommendation-with-exposure-constraints&quot;&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2305.12319&quot;&gt;Multi-channel Integrated Recommendation with Exposure Constraints&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Alibaba のホームフィードに掲載するアイテム配置の最適化に関する論文。ホーム面には複数のチャネルからの出稿があり、それぞれ独立に評価値がついている。ホーム面 utility を最大化したいが、その一方で、それぞれのチャネルの露出量に対する上限下限のビジネス制約もある。提案としては、utility 最大化のための主問題と、制約を満たすための双対問題を iterative に解くという形。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ct4rec-simple-yet-effective-consistency-training-for-sequential-recommendation&quot;&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2112.06668&quot;&gt;CT4Rec: Simple yet Effective Consistency Training for Sequential Recommendation&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;系列推薦モデルの精度向上のための正則化手法の提案。ベースのモデルは埋め込みの類似度を利用するものであればなんでもよく、異なる dropout で2回 forward 計算をし、得られた同一のユーザーベクトル同士が近くなるようにする。具体的には、二つのベクトル間の KL divergence と、バッチ内での他のユーザーベクトルとの類似度分布に対する KL divergence の二つを導入している。アイディアはシンプルなので、機会があったら実装してみたい。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;an-empirical-study-of-selection-bias-in-pinterest-ads-retrieval&quot;&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;dl.acm.org&#x2F;doi&#x2F;10.1145&#x2F;3580305.3599771&quot;&gt;An Empirical Study of Selection Bias in Pinterest Ads Retrieval&lt;&#x2F;a&gt;&lt;&#x2F;h2&gt;
&lt;p&gt;Pinterest の広告推薦モデルにおける retrieval ステージのバイアス除去に関する論文。Retrieval の推論時の candidate pool は targeting された全広告だが、学習は ranker がスコアをつけたものしかなく、バイアスが生じる原因となっている。結果を見ると、提案手法のドメイン適応も結構良いが、普通に in-batch negative sampling でも十分良さそうに見えてしまう。&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>このサイトのホスト先を GitHub Pages から Cloudflare Pages へ移行した</title>
		<published>2023-11-11T00:00:00+00:00</published>
		<updated>2023-11-11T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2023/github-pages-to-cloudflare-pages/"/>
		<id>https://yng87.page/blog/2023/github-pages-to-cloudflare-pages/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2023/github-pages-to-cloudflare-pages/">&lt;p&gt;タイトルの通り、このサイトのホスティング先を &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;pages.cloudflare.com&#x2F;&quot;&gt;Cloudflare Pages&lt;&#x2F;a&gt; へ移行した。
これまで使っていた GitHub Pages だと無料でプライベートリポジトリをホストできないのが地味に不満だった。書きかけの記事が見えてしまうし、個人情報を後で消そうとしてもコミット履歴が残ってしまう（まあ誰も訪問していないようなサイトだとは思うが）。
それから、Google domains が売却されるので、ドメインを Cloudflare に移管したのだが、良い機会なのでホスティング先も見直そうと思ったのもある。
選択肢としては他にも netlify とか色々あると思うが、更新頻度も訪問数も非常に小さいサイトなので正直どこを選んでも同じな気がする。&lt;&#x2F;p&gt;
&lt;p&gt;移行は驚くほどスムーズで、ドキュメントをちゃんと読まずとも、コンソールに従ってリポジトリを登録するだけでデプロイできた。このサイトは &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;Zola&lt;&#x2F;a&gt; でビルドしているので、&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;developers.cloudflare.com&#x2F;pages&#x2F;framework-guides&#x2F;deploy-a-zola-site&#x2F;#deploy-with-cloudflare-pages&quot;&gt;以下のような設定をすれば良い&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;Build command: &lt;code&gt;zola build&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;Build directory: &lt;code&gt;public&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;li&gt;環境変数: &lt;code&gt;ZOLA_VERSION=&amp;lt;好きな Zola version&amp;gt;&lt;&#x2F;code&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;一点はまったのはマシンタイプ。&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;developers.cloudflare.com&#x2F;pages&#x2F;platform&#x2F;language-support-and-tools&#x2F;&quot;&gt;Latest である version 2 の Production build system には &lt;code&gt;zola&lt;&#x2F;code&gt; バイナリが入っていないので、version 1 を選択する必要がある&lt;&#x2F;a&gt;。&lt;&#x2F;p&gt;
&lt;p&gt;色々新しくすると気分も一新するので、もうちょっと気楽にブログを更新していきたいところ。&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>使える電池を選ぶための最小の試行回数を求める</title>
		<published>2023-06-30T00:00:00+00:00</published>
		<updated>2023-06-30T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2023/graphspuzzle/"/>
		<id>https://yng87.page/blog/2023/graphspuzzle/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2023/graphspuzzle/">&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;bkamins.github.io&#x2F;julialang&#x2F;2023&#x2F;06&#x2F;23&#x2F;graphspuzzle.html&quot;&gt;The return of the graphs (and an interesting puzzle)&lt;&#x2F;a&gt; というブログ記事に載っていた問題が面白かったので紹介したい。日本語訳 (by DeepL) をすると&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;あなたは8個の電池を与えられ、そのうち4個は良品、4個は消耗品であるが、見た目には違いがない。 あなたは懐中電灯を持っており、その懐中電灯を作動させるためには良品の電池2個が必要である。 あなたは懐中電灯を作動させたい。最悪の場合、懐中電灯が確実に作動するために、2本の電池を懐中電灯に入れる回数は何回必要か。&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;電池は全部で8個あり、そこから2つ選ぶ方法は28通りで、良品の4つから選ぶのは6通りなので、何も考えず懐中電灯に入れていっても23回目には確実に動作させることができる。&lt;&#x2F;p&gt;
&lt;p&gt;が、もちろんもっと少ない試行回数で済ませることが可能で、正解は7だ。&lt;&#x2F;p&gt;
&lt;p&gt;7回で十分なことはすぐにわかる。1から8までの番号を振った電池を、&lt;code&gt;{1, 2, 3}&lt;&#x2F;code&gt;, &lt;code&gt;{4, 5, 6}&lt;&#x2F;code&gt;, &lt;code&gt;{7, 8}&lt;&#x2F;code&gt; の3グループに分けると、どこかのグループには少なくとも二つの良品が入る。なので、各グループ内で全ペアを試していけばよく、その回数は $3+3+1=7$ だ。&lt;&#x2F;p&gt;
&lt;p&gt;6回では不十分なことの証明は少しややこしいので、冒頭のブログを見てほしい。&lt;&#x2F;p&gt;
&lt;p&gt;面白いのはこの問題は、グラフの問題としてもアプローチできることだ。ノード数8のグラフを用意し、ノード $i$ と $j$ の間にエッジがあるとき、対応する電池を懐中電灯に入れると考える。&lt;&#x2F;p&gt;
&lt;p&gt;例えば、7回の比較でうまくいくパターンは以下のようなグラフとして表現できる。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  1      4    7&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; &#x2F; \    &#x2F; \   |&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2---3  5---6  8&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;そうすると、考えるべき問題は「ノードが8個、エッジが $E$ 本の無向グラフにおいて、ノードの部分集合でその間にエッジがないもの (independent set と呼ぶ) の最大サイズを3以下にすることができるか？」になる。もしサイズ4以上の independent set があれば、良品が全てそこに入った場合、良品ペアを発見できないことになる。逆にどんな independent set もサイズが3以下になるなら条件を満たせる。&lt;&#x2F;p&gt;
&lt;p&gt;例えばエッジ7本でも&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  1      4    7&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; &#x2F; \    &#x2F; \&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;2---3--5---6  8&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;のようなグラフを作ってしまうと、&lt;code&gt;{1, 4, 7, 8}&lt;&#x2F;code&gt; がサイズ4の independent set になり、これらが良品の場合に発見できないことになる。&lt;&#x2F;p&gt;
&lt;p&gt;このような問題に帰着できればあとは計算機の力でゴリ押して解くことができる。エッジの本数ごとにありうるグラフをすべて列挙して条件を満たすかを調べれば良い。具体的には、エッジが0本のグラフから初めて以下の手順を繰り返す：&lt;&#x2F;p&gt;
&lt;ol&gt;
&lt;li&gt;エッジ $E$ 本のグラフにあらゆるパターンでエッジを一本加えてエッジ $E+1$ 本のグラフをすべて列挙する。同型なグラフは一つにまとめる&lt;&#x2F;li&gt;
&lt;li&gt;各グラフについてノードの independent set の最大サイズを求める&lt;&#x2F;li&gt;
&lt;li&gt;エッジ $E+1$ 本のグラフ全体におけるこの最大サイズの最小値を求める&lt;&#x2F;li&gt;
&lt;li&gt;この最小値が3以下になれば終了&lt;&#x2F;li&gt;
&lt;&#x2F;ol&gt;
&lt;p&gt;ブログに載っていた Julia コードが自分の環境で動かなかったので、以下に Rust に書き直したものを貼っておく&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;yng87&#x2F;8fe7bff1725a28ffbc332f3632185e61&quot;&gt;Graphs puzzle&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>モデルが生成したデータを学習に繰り返し使うとどうなるか</title>
		<published>2023-06-28T00:00:00+00:00</published>
		<updated>2023-06-28T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2023/gaussian-mixture-collapse/"/>
		<id>https://yng87.page/blog/2023/gaussian-mixture-collapse/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2023/gaussian-mixture-collapse/">&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2305.17493v2&quot;&gt;The Curse of Recursion: Training on Generated Data Makes Models Forget&lt;&#x2F;a&gt; という論文が &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;importai.substack.com&#x2F;p&#x2F;import-ai-333-synthetic-data-makes&quot;&gt;Import AI で紹介されていて&lt;&#x2F;a&gt;面白かったのでざっくり自分でも確かみてみた。&lt;&#x2F;p&gt;
&lt;p&gt;内容としては、モデルが生成したデータを別のモデルの学習に使うということを繰り返していくと、次第に分布の tail の情報が失われていって、出力の範囲が狭まってしまうという指摘。特に生成 AI の結果がインターネットに溢れ始めた昨今では重要になってくるはず。&lt;&#x2F;p&gt;
&lt;p&gt;論文では混合ガウス分布等を例に取って、理論解析と数値解析をしており、この現象を model collapse と呼んでいる。数値解析の方は簡単にできそうだったので自分でも実装してみた。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;python-shi-zhuang&quot;&gt;Python 実装&lt;&#x2F;h2&gt;
&lt;p&gt;やりたいことは、一次元の混合ガウス分布のパラメータフィットをして、フィットされた分布から新しくデータを生成し、それを元に分布を再びフィットするということを何度も繰り返すことだ。&lt;&#x2F;p&gt;
&lt;p&gt;具体的には、第 $i$ ステップの学習データがある時に、以下三つを混ぜて第 $i+1$ ステップの学習データを構成する&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;第 $i$ ステップにおける学習データ&lt;&#x2F;li&gt;
&lt;li&gt;第 $i$ ステップの学習データを使ってフィットした混合ガウス分布からのサンプル&lt;&#x2F;li&gt;
&lt;li&gt;真の分布からのサンプル&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;$i=0$ では真の混合ガウス分布からのサンプルとし、三つのデータの比率は適当に変えて良い。Python で実装すると、以下の &lt;code&gt;sample_next_gen&lt;&#x2F;code&gt; 関数のようになる:&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; collections&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; namedtuple&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; numpy&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; as&lt;&#x2F;span&gt;&lt;span&gt; np&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; matplotlib&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;pyplot&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; as&lt;&#x2F;span&gt;&lt;span&gt; plt&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; sklearn&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;mixture&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; GaussianMixture&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;from&lt;&#x2F;span&gt;&lt;span&gt; tqdm&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; import&lt;&#x2F;span&gt;&lt;span&gt; tqdm&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;GaussianMixtureParams&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; namedtuple&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;GaussianMixtureParams&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;mus&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;sigmas&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;#39;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;mixture_ratio&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;#39;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; sample_from_gaussian_mixture&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;gm_params&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; num_samples&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    mus&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; np&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;array&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;gm_params&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;mus&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    sigmas&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; np&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;array&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;gm_params&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;sigmas&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    num_mixture&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; len&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;gm_params&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;mixture_ratio&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    mixture_idx&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; np&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;random&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;choice&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;num_mixture&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; size&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;num_samples&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; p&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;gm_params&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;mixture_ratio&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    standard_normal_noises&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; np&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;random&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;normal&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;size&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;num_samples&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    samples&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; mus&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;mixture_idx&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt; standard_normal_noises&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; sigmas&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;mixture_idx&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; samples&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; fit_gaussian_mixture&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;samples&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; n_components&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; random_state&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    gm&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; GaussianMixture&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;n_components&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;n_components&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; random_state&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;random_state&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;fit&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;samples&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;reshape&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    mus&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; gm&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;means_&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;reshape&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    sorted_idx&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; np&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;argsort&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;mus&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    gm_params&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; GaussianMixtureParams&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        gm&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;means_&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;reshape&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;sorted_idx&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        np&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;sqrt&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;gm&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;covariances_&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;reshape&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;sorted_idx&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        gm&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;weights_&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;sorted_idx&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    )&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; gm_params&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-storage z-type&quot;&gt;def&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; sample_next_gen&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt;gm_params_org&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; gm_params_fitted&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; samples_current_gen&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; ratio_org&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; ratio_fitted&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable z-parameter z-function&quot;&gt; num_samples&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; オリジナルの分布、フィットされた分布、最新のサンプルからのサンプリングを使う&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    num_samples_org&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;num_samples&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; ratio_org&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    num_samples_fitted&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; int&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;num_samples&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; *&lt;&#x2F;span&gt;&lt;span&gt; ratio_fitted&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    num_samples_current&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; num_samples&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; -&lt;&#x2F;span&gt;&lt;span&gt; num_samples_org&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; -&lt;&#x2F;span&gt;&lt;span&gt; num_samples_fitted&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span&gt; num_samples_current&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;        raise&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; ValueError&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Invalid ratio&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    samples_org&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; sample_from_gaussian_mixture&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;gm_params_org&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; num_samples_org&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    samples_fitted&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; sample_from_gaussian_mixture&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;gm_params_fitted&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; num_samples_fitted&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    samples_current&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; np&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;random&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;choice&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;samples_current_gen&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; size&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;num_samples_current&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt; replace&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;False&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; np&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;concatenate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;samples_org&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; samples_fitted&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; samples_current&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h2 id=&quot;jie-guo&quot;&gt;結果&lt;&#x2F;h2&gt;
&lt;p&gt;各回 $N=10000$ のサンプリングを合計2000ステップ実行してみた。真の混合ガウス分布は component 二つでパラメータは&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;$\boldsymbol \mu = (-2, 2)$&lt;&#x2F;li&gt;
&lt;li&gt;$\boldsymbol \sigma = (1, 1)$&lt;&#x2F;li&gt;
&lt;li&gt;混合比 1:1&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;とした。簡単のため、学習データは全て前回ステップでフィットした分布から生成した。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;num_samples&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 10000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;num_gen&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 2000&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;gm_params_org&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; GaussianMixtureParams&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;-&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0.5&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0.5&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;samples&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; sample_from_gaussian_mixture&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;gm_params_org&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; num_samples&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; _&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; in&lt;&#x2F;span&gt;&lt;span&gt; tqdm&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;range&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;num_gen&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    gm_params_fitted&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; fit_gaussian_mixture&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;samples&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 2&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    samples&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; sample_next_gen&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;gm_params_org&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; gm_params_fitted&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; samples&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0.0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1.0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; num_samples&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;フィットされた分散の推移をプロットしてみると
&lt;img src=&quot;&#x2F;blog&#x2F;2023&#x2F;gm_sigmas.png&quot; alt=&quot;混合ガウス分布の分散推移&quot; &#x2F;&gt;
となり確かにより狭い分布に向かっている様子がわかる。&lt;&#x2F;p&gt;
&lt;p&gt;また初回のヒストグラムと、2000ステップ目でのヒストグラムを比較してみると、混合比も片方の山に偏っていっているように見える。
&lt;img src=&quot;&#x2F;blog&#x2F;2023&#x2F;gm_hist.png&quot; alt=&quot;初回と 2000 ステップ目の混合ガウス分布のヒストグラム&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;ただ、乱数シードを変えて何回か繰り返したところフィットされた分布がより広い方向に向かうこともあったし、三種類のデータの混合比によって model collapse の速度も変わったりした。どれくらい現実の LLM などに当てはまる結果なのかはわからない。&lt;&#x2F;p&gt;
&lt;p&gt;ノートブックへのリンクは&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;gist.github.com&#x2F;yng87&#x2F;b69d8e93f73c42c84d84d1b7840326d7&quot;&gt;こちら&lt;&#x2F;a&gt;。間違いがあったら教えてください。&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>ベイズファクターによる仮説検定</title>
		<published>2023-05-03T00:00:00+00:00</published>
		<updated>2023-05-03T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2023/hypothesis-testing-freq-and-bayes/"/>
		<id>https://yng87.page/blog/2023/hypothesis-testing-freq-and-bayes/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2023/hypothesis-testing-freq-and-bayes/">&lt;p&gt;ベイズ統計に基づく仮説検定の手法として、ベイズファクターと呼ばれる量を用いるものがある。頻度論による検定の問題を克服する手段としてベイズが挙げられるときは、ベイズファクターのような手法を想定している場面が多いと思う。仕事ではこれまで頻度論による検定で間に合ってしまう場面が多くあまり触れてこなかったが、最近になってようやく勉強したので、簡単にまとめておきたい。&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;
&lt;h2 id=&quot;jia-shuo-jian-ding-nofang-fa&quot;&gt;仮説検定の方法&lt;&#x2F;h2&gt;
&lt;p&gt;ベイズファクターによる検定を理解するために、古典的な頻度論による手法や、ベイズ信用区間を利用したものとの比較をしてみる。解析的に計算可能な例を持ち出すのがわかりやすいと思うので、単純なコイン投げ実験を考えてみよう。コインを $N$ 回投げて、表が $m$ 回でたとき、このコインに歪みがないかどうかを判定したいとする。表の出る確率を $\mu$ とすると、&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;帰無仮説 ($H_0$): $\mu = 0.5$&lt;&#x2F;li&gt;
&lt;li&gt;対立仮説 ($H_1$): $\mu \neq 0.5$&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;のどちらを選ぶかという問題になる。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pin-du-lun-nojia-shuo-jian-ding&quot;&gt;頻度論の仮説検定&lt;&#x2F;h3&gt;
&lt;p&gt;まずは頻度論による検定を簡単にまとめてみる。頻度論では $H_0$ のもとで検定統計量の従う確率分布を求め、観測値が得られる確率 (&lt;em&gt;p-value&lt;&#x2F;em&gt;) を計算する。コイン投げの場合、&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Binomial_test&quot;&gt;二項検定&lt;&#x2F;a&gt;や&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Chi-squared_test&quot;&gt;カイ二乗検定&lt;&#x2F;a&gt;を実行すればよいだろう。P-value を計算し、それが事前に決めた基準値より小さい場合に、背理法的に $H_0$ を棄却する。&lt;&#x2F;p&gt;
&lt;p&gt;頻度論による検定の特徴を挙げてみる:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;通常、統計量の従う分布が解析的に計算できる場合を扱う。そうでないときもサンプル数が多ければ、中心極限定理を介して適用できることもある。そのため、軽い数値計算で分析できることが多い。&lt;&#x2F;li&gt;
&lt;li&gt;帰無仮説を棄却することはできても採択することはできない。頻度論の検定は背理法と同様の構造なので、「矛盾があるから仮定が偽」と結論することはできるが、積極的に帰無仮説が真と主張することはできない。&lt;&#x2F;li&gt;
&lt;li&gt;（今回は深入りしないが）&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Likelihood_principle&quot;&gt;尤度原理&lt;&#x2F;a&gt;に反する。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;次にベイズ統計を使った方法の一つである、信用区間を使った検定について見ていこう。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;beizuxin-yong-qu-jian&quot;&gt;ベイズ信用区間&lt;&#x2F;h3&gt;
&lt;p&gt;ベイズ統計を使った検定方法の一つに、信用区間を利用するものがある。これは統計モデルのパラメータ推定の結果を検定に応用する手法である。&lt;&#x2F;p&gt;
&lt;p&gt;ここではコイン投げ実験を二項分布でモデリングすることにしよう。表の出る確率 $\mu$ の事前分布はベータ分布に設定する
$$
\begin{aligned}
m &amp;amp;\sim \mathrm{B}(N, \mu), \\
\mu &amp;amp;\sim \mathrm{Beta}(a, b).
\end{aligned}
$$&lt;&#x2F;p&gt;
&lt;p&gt;ここで $a$, $b$ はハイパーパラメータである。よく知られているように、ベータ分布は二項分布に対する共役事前分布になっており、事後分布が解析的に計算できる:
$$
\mu \sim \mathrm{Beta}(m+a, N-m+b).
$$&lt;&#x2F;p&gt;
&lt;p&gt;仮説検定として使う際は、事後分布で $\mu$ が高い確率で含まれている区間（&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;en.wikipedia.org&#x2F;wiki&#x2F;Credible_interval&quot;&gt;信用区間&lt;&#x2F;a&gt;）を計算し、そこに帰無仮説が含まれているかどうかを見る。今回の例では帰無仮説は $\mu=0.5$ であり、この値が信用区間の外にあれば帰無仮説を棄却することができる。逆に区間の内側にあれば、データが積極的に対立仮説を支持するとは言えないということになる。&lt;&#x2F;p&gt;
&lt;p&gt;この信用区間を使った方法には以下の特徴がある&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;$\mu$ の確率分布を計算できる。そのため得られた信用区間を「パラメータがある確率で含まれる区間」と素直に解釈できる。頻度論では $\mu$ は確率変数ではなかったので、分布を計算することはできない。頻度論でも同様の概念として信頼区間があるが、$\mu$ が確率変数ではないので解釈に注意する必要がある。&lt;&#x2F;li&gt;
&lt;li&gt;一般的に、サンプル数が大きくなるほど、事前分布の影響が減少していく。上の例では $m, N-m \gg a, b$ であれば、結果は $a, b$ の選択に鈍感になる。&lt;&#x2F;li&gt;
&lt;li&gt;事後分布を得るには、一般的には MCMC などの重い数値計算が必要となる。&lt;&#x2F;li&gt;
&lt;li&gt;帰無仮説と対立仮説を対等に扱っているわけではない。上の例では、帰無仮説は $\mu=0.5$ という一点であるが、事後分布 $\mathrm{Beta}(m+a, N-m+b)$ のもとで $\mu$ がこの値を取る確率は常にゼロである。そのため帰無仮説を採択できるかどうかが焦点になるようなケースには向かないらしい。この点に関しては、&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;link.springer.com&#x2F;article&#x2F;10.3758&#x2F;s13423-017-1420-7&quot;&gt;Rouder, Haaf and Vandekerckhove (2018)&lt;&#x2F;a&gt; で詳しく論じられている。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h3 id=&quot;beizuhuakuta&quot;&gt;ベイズファクター&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;gai-yao&quot;&gt;概要&lt;&#x2F;h4&gt;
&lt;p&gt;信用区間を使った方法は、ベイズ統計を使っているとはいえ、実質は確率分布のパラメータ推定だった。頻度論よりも得られた区間推定の結果が解釈しやすいなどの特徴はあるが、帰無仮説を積極的に採択する用途には向かないという事情は同じであった。&lt;&#x2F;p&gt;
&lt;p&gt;一方、ベイズファクターはベイズ統計のモデル比較の考え方を用いる手法であり、この困難を克服する事ができる。&lt;&#x2F;p&gt;
&lt;p&gt;我々が本当に評価したいのは、データ $\mathcal{D}$ が与えられた下での、仮説 $H$ の事後確率 $p(H|\mathcal{D})$ である。ベイズでは仮説は統計モデルで表す必要がある。ここではそれを $\mathcal{M}$ としよう。ベイズの定理を用いるとモデル $\mathcal{M}$ の事後確率は
$$
p(\mathcal{M}|\mathcal{D}) = \frac{p(\mathcal{D}|\mathcal{M})p(\mathcal{M})}{p(\mathcal{D})}
$$
とかける。帰無仮説と対立仮説でこの量の比を取ると&lt;&#x2F;p&gt;
&lt;p&gt;$$
\frac{p(\mathcal{M}_1|\mathcal{D})}{p(\mathcal{M}_0|\mathcal{D})}
= \frac{p(\mathcal{D}|\mathcal{M}_1)}{p(\mathcal{D}|\mathcal{M}_0)}
\times \frac{p(\mathcal{M}_1)}{p(\mathcal{M}_0)}
$$
となる。この右辺第一項
$$
BF_{10} = \frac{p(\mathcal{D}|\mathcal{M}_1)}{p(\mathcal{D}|\mathcal{M}_0)}
$$
がベイズファクターと呼ばれる量であり、marginal likelihood $p(\mathcal{D}|\mathcal{M})$ の比になっている。$p(\mathcal{M}_1)&#x2F;p(\mathcal{M}_0)$ はそれぞれのモデルに対する事前の信念の比であり、ベイズファクターが計算できると、このオッズをデータで更新できるということだ。&lt;&#x2F;p&gt;
&lt;p&gt;特に、事前オッズを 1 にすれば、ベイズファクターが1からどれだけ離れているかで、仮説を検定できる。1 より十分大きければ対立仮説を採択し、逆に1より十分小さければ帰無仮説を採択する。これまでの手法はいわばヒューリスティックなのだが、ベイズファクターはモデルの事後確率を、確率の法則に従って自然に評価しているという点で、理論的には素直で気持ちが良い。&lt;&#x2F;p&gt;
&lt;p&gt;ベイズファクターも検定である以上、値がいくつ以上なら仮説を採択・棄却するのかは業界や現場で基準を決める必要がある。例えば &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.jstor.org&#x2F;stable&#x2F;2291091&quot;&gt;Kass and Raftery (1995)&lt;&#x2F;a&gt; によく使われる基準がいくつかまとめられている。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;marginal-likelihood&quot;&gt;Marginal likelihood&lt;&#x2F;h4&gt;
&lt;p&gt;ベイズファクターで肝になるのは、marginal likelihood である。コイン投げの例を使ってこれを計算してみよう。信用区間の場合と同様に、コイン投げを二項分布でモデルすると、$\mathcal{M}_0$ のもとでは $\mu=1&#x2F;2$ なので
$$
p(\mathcal{D}|\mathcal{M}_0) = \binom{N}{m}2^{-N}
$$
となる。&lt;&#x2F;p&gt;
&lt;p&gt;一方、$\mathcal{M}_1$ の場合は話が少々複雑になる。$\mu \neq 1&#x2F;2$ は連続量なので、以下の積分を実行しなければならない:
$$
p(\mathcal{D}|\mathcal{M}_1) = \int_0^1d\mu\ p(\mathcal{D}|\mathcal{M}_1, \mu)p(\mu|\mathcal{M}_1).
$$&lt;&#x2F;p&gt;
&lt;p&gt;尤度 $p(\mathcal{D}|\mathcal{M}_1, \mu)$ には前と同様二項分布
$$
p(\mathcal{D}|\mathcal{M}_1, \mu) = \binom{N}{m}\mu^m(1-\mu)^{N-m},
$$
を採用し、$\mu$ の事前分布 $p(\mu|\mathcal{M}_1)$ にはベータ分布 $\mathrm{Beta}(a, b)$ を仮定してみよう。ベータ分布の密度関数は
$$
p(\mu|\mathcal{M}_1) = \frac{\Gamma(a+b)}{\Gamma(a)\Gamma(b)}\mu^{a-1}(1-\mu)^{b-1}
$$
である。すると marginal likelihood は解析的に計算できて
$$
p(\mathcal{D}|\mathcal{M}_1) = \frac{N!}{m!(N-m)!}\cdot\frac{\Gamma(a+b)}{\Gamma(a)\Gamma(b)}\cdot\frac{\Gamma(m+a)\Gamma(N-m+b)}{\Gamma(a+b+N)}
$$
となることがわかる。特に $m, N-m \gg a, b$ の場合は
$$
\ln p(\mathcal{D}|\mathcal{M}_1) \simeq \ln\left(\frac{\Gamma(a+b)}{\Gamma(a)\Gamma(b)}\right) +(a-1)\ln m+(b-1)\ln(N-m)-(a+b-1)\ln N,
$$
が得られる。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;shi-qian-fen-bu-henoyi-cun&quot;&gt;事前分布への依存&lt;&#x2F;h4&gt;
&lt;p&gt;この式を見ると分かる通り、marginal likelihood は、$\mu$ の事前分布 $p(\mu|\mathcal{M}_1)$ に依存する。特に、&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.jstor.org&#x2F;stable&#x2F;2291091&quot;&gt;サンプル数が大きくなっても事前分布の影響が残り続ける&lt;&#x2F;a&gt;。もちろん事前分布としてベータ分布以外のものを選んでも良いので、この式で表されるよりもさらに大きい自由度がある。&lt;&#x2F;p&gt;
&lt;p&gt;ベイズ信用区間の場合、サンプル数を増やしていくと事前分布の影響は減衰する傾向にあった。一方 marginal likelihood は尤度に事前分布をかけて積分した量なので、事前分布が狭すぎても広すぎても小さな値になってしまう。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#prml&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; そのため、例えば安易に無情報な分布を用いると、対立仮説の marginal likelihood が小さく評価され、帰無仮説がより支持されるようになる。&lt;&#x2F;p&gt;
&lt;p&gt;このような性質があるため、ベイズファクターを仮説検定に用いる際には、事前分布の設計にドメイン知識を活用したり、コミュニティでの合意が取れているような分布を利用したりというのが大事になる。また事前分布の選択に関する sensitivity analysis も必要となる。一応この負担を軽減するために default prior と呼ばれる分布が設計されているらしいが、&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;vasishth.github.io&#x2F;bayescogsci&#x2F;book&#x2F;ch-bf.html&quot;&gt;それらを盲目的に使うことへの批判&lt;&#x2F;a&gt;もある。&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.reddit.com&#x2F;r&#x2F;statistics&#x2F;comments&#x2F;10qpo9v&#x2F;q_how_can_bayes_factor_be_compatible_with_the&#x2F;&quot;&gt;Reddit&lt;&#x2F;a&gt; にも色々と事前分布に関する議論があるし、もう少し実務に近いところだと、&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;dl.acm.org&#x2F;doi&#x2F;10.1145&#x2F;2740908.2742563&quot;&gt;BingのA&#x2F;Bテストの事例&lt;&#x2F;a&gt;でも、過去の実験結果を使って事前分布を構成することの重要性が主張されている。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;sonota-note-zheng&quot;&gt;その他の特徴&lt;&#x2F;h4&gt;
&lt;p&gt;事前分布への敏感性以外に、ベイズファクターの特徴をまとめてみよう&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;ベイズファクターでは、帰無仮説を採択することができる。これは、帰無仮説と対立仮説を対等に扱っているからである。&lt;&#x2F;li&gt;
&lt;li&gt;信用区間の場合と同様、一般に積分を解析的に実行することはできないので何らかの数値計算が必要となる。R には、このための &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;cran.r-project.org&#x2F;web&#x2F;packages&#x2F;BayesFactor&#x2F;vignettes&#x2F;manual.html&quot;&gt;BayesFactor&lt;&#x2F;a&gt; というパッケージがある。&lt;&#x2F;li&gt;
&lt;li&gt;任意の2つの仮説を比較できる。頻度論やベイズ信用区間の場合、帰無仮説と対立仮説のコイン投げ分布は同じ二項分布で、そのパラメータ $\mu$ の値がどうなるかを調べていたが、ベイズファクターでは全く異なる分布を持つようなモデル同士の比較も可能。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;matome&quot;&gt;まとめ&lt;&#x2F;h2&gt;
&lt;p&gt;以上の内容を表にまとめてみた&lt;&#x2F;p&gt;
&lt;table&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;手法&lt;&#x2F;th&gt;&lt;th&gt;帰無仮説の採択&lt;&#x2F;th&gt;&lt;th&gt;任意の仮説同士の比較&lt;&#x2F;th&gt;&lt;th&gt;Prior への依存&lt;&#x2F;th&gt;&lt;th&gt;計算コスト&lt;&#x2F;th&gt;&lt;&#x2F;tr&gt;&lt;&#x2F;thead&gt;&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;頻度論&lt;&#x2F;td&gt;&lt;td&gt;不可&lt;&#x2F;td&gt;&lt;td&gt;不可&lt;&#x2F;td&gt;&lt;td&gt;-&lt;&#x2F;td&gt;&lt;td&gt;小&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;ベイズ信用区間&lt;&#x2F;td&gt;&lt;td&gt;不可&lt;&#x2F;td&gt;&lt;td&gt;不可&lt;&#x2F;td&gt;&lt;td&gt;小&lt;&#x2F;td&gt;&lt;td&gt;一般に大&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;tr&gt;&lt;td&gt;ベイズファクター&lt;&#x2F;td&gt;&lt;td&gt;可&lt;&#x2F;td&gt;&lt;td&gt;可&lt;&#x2F;td&gt;&lt;td&gt;大&lt;&#x2F;td&gt;&lt;td&gt;一般に大&lt;&#x2F;td&gt;&lt;&#x2F;tr&gt;
&lt;&#x2F;tbody&gt;&lt;&#x2F;table&gt;
&lt;p&gt;個人的には、計算や sensitivity analysis の大変さを考慮すると、実用上ベイズファクターを使うのは、帰無仮説を採択したいなど他の手法では分析ができないケースに限られそうという印象。&lt;&#x2F;p&gt;
&lt;p&gt;よくある RCT の効果検証であれば、効果量とサンプルサイズ設計をしっかりした上で頻度論の仮説検定を使うのが楽だと思う。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;references&quot;&gt;References&lt;&#x2F;h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;doi.org&#x2F;10.3758&#x2F;s13423-017-1420-7&quot;&gt;Bayesian inference for psychology, part IV  parameter estimation and Bayes factors&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;vasishth.github.io&#x2F;bayescogsci&#x2F;book&#x2F;ch-bf.html&quot;&gt;Chapter 15 Bayes factors | An Introduction to Bayesian Data Analysis for Cognitive Science&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.jstor.org&#x2F;stable&#x2F;2291091&quot;&gt;Bayes factor&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;blog.logicoffee.tech&#x2F;posts&#x2F;math&#x2F;different-types-of-hypothesis-testing.html&quot;&gt;仮説検定の手法ごとに結論がどれほど変わるか比べてみた | Blogicoffee&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;qiita.com&#x2F;hokudai_meiyo&#x2F;items&#x2F;038b55e0380c3f653640&quot;&gt;ベイズ統計の仮説検定 - Qiita&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.jstage.jst.go.jp&#x2F;article&#x2F;sjpr&#x2F;61&#x2F;1&#x2F;61_101&#x2F;_pdf&quot;&gt;ベイズファクターによる心理学的仮説・モデルの評価&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;hr &#x2F;&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;prml&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;例えば PRML の三章に直感的な説明がある。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>Python の型ヒントを理解したい</title>
		<published>2023-01-13T00:00:00+00:00</published>
		<updated>2023-01-13T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2023/type-system-python/"/>
		<id>https://yng87.page/blog/2023/type-system-python/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2023/type-system-python/">&lt;p&gt;仕事で漫然と Python に型ヒントをつけていると、type checker の警告を理解できないことがある。実際に遭遇したケースの一つをかなり単純化して書いてみよう&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;x&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; list&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;float&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;y&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; list&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; y&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;これを mypy にチェックさせると&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#mypy-online&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;main.py:3: error: Incompatible types in assignment (expression has type &amp;quot;List[int]&amp;quot;, variable has type &amp;quot;List[float]&amp;quot;)  [assignment]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;main.py:3: note: &amp;quot;List&amp;quot; is invariant -- see https:&#x2F;&#x2F;mypy.readthedocs.io&#x2F;en&#x2F;stable&#x2F;common_issues.html#variance&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;main.py:3: note: Consider using &amp;quot;Sequence&amp;quot; instead, which is covariant&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Found 1 error in 1 file (checked 1 source file)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;という、型の不整合を示すエラーが出る。&lt;code&gt;int&lt;&#x2F;code&gt; は &lt;code&gt;float&lt;&#x2F;code&gt; に代入できるので、当然 &lt;code&gt;list[int]&lt;&#x2F;code&gt; は &lt;code&gt;list[float]&lt;&#x2F;code&gt; に代入できるのでは…？と思ってしまうのだが実はそうではないということらしい。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#not-runtime&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;
エラーメッセージを読むと、&lt;code&gt;&quot;List&quot; is invariant&lt;&#x2F;code&gt; や &lt;code&gt;Consider using &quot;Sequence&quot; instead, which is covariant&lt;&#x2F;code&gt; とあるが、なんだそれとなってしまった。&lt;&#x2F;p&gt;
&lt;p&gt;流石にこれは良くないと思ったので、読むべきとされていそうな資料を一通り読んでみた。具体的にはこの辺り&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;peps.python.org&#x2F;pep-0483&#x2F;&quot;&gt;PEP 483 – The Theory of Type Hints | peps.python.org&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;peps.python.org&#x2F;pep-0484&#x2F;&quot;&gt;PEP 484 – Type Hints | peps.python.org&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;mypy.readthedocs.io&#x2F;en&#x2F;stable&#x2F;generics.html#variance-of-generics&quot;&gt;Variance of generic types - mypy 0.991 documentation&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;mypy.readthedocs.io&#x2F;en&#x2F;stable&#x2F;common_issues.html#invariance-vs-covariance&quot;&gt;Invariance vs covariance - mypy 0.991 documentation&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;これらに目を通すと分かるのだが、Python の型チェックでは、部分的に型がついているプログラムの解析をうまくやる必要があるという点で事情が多少複雑で、そのために gradual typing などという概念が発明されたりしている。しかし今回はそういう深い話には踏み込まず、冒頭の例がなぜダメなのかと言う点に絞って書こうと思う。&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;
&lt;h2 id=&quot;nazeeragachu-runoka&quot;&gt;なぜエラーが出るのか？&lt;&#x2F;h2&gt;
&lt;p&gt;結論から話すと、冒頭のコードで mypy がエラーを出すのは、&lt;code&gt;list[int]&lt;&#x2F;code&gt; が &lt;code&gt;list[float]&lt;&#x2F;code&gt; の &lt;strong&gt;subtype ではない&lt;&#x2F;strong&gt;からだ。Subtype は文字通り型の間のヒエラルキーを表すもので、PEP 483 では、 &lt;code&gt;second_type&lt;&#x2F;code&gt; という型が &lt;code&gt;first_type&lt;&#x2F;code&gt; の subtype であることを以下のように定めている&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;every value from &lt;code&gt;second_type&lt;&#x2F;code&gt; is also in the set of values of &lt;code&gt;first_type&lt;&#x2F;code&gt;; and&lt;&#x2F;li&gt;
&lt;li&gt;every function from &lt;code&gt;first_type&lt;&#x2F;code&gt; is also in the set of functions of &lt;code&gt;second_type&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;一つ目の条件は代入先の型が、代入元の値を持っていなければならないというもので、 &lt;code&gt;int&lt;&#x2F;code&gt; と &lt;code&gt;float&lt;&#x2F;code&gt; の関係を思い出せばわかりやすいだろう。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#int-float&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; 冒頭に述べた &lt;code&gt;list[int]&lt;&#x2F;code&gt; と &lt;code&gt;list[float]&lt;&#x2F;code&gt; の例もこの条件は満たしている。&lt;&#x2F;p&gt;
&lt;p&gt;問題になるのは二つ目だ。リストの場合これは、&lt;code&gt;list[float]&lt;&#x2F;code&gt; に作用する関数は、&lt;code&gt;list[int]&lt;&#x2F;code&gt; にも作用できなければならないということを言っている。つまり、代入元に作用する関数は、代入先にも作用できなければいけないということだ。しかし、例えば &lt;code&gt;x.append(1.0)&lt;&#x2F;code&gt; という操作を考えてみると、これは &lt;code&gt;list[float]&lt;&#x2F;code&gt; には行えるが、&lt;code&gt;list[int]&lt;&#x2F;code&gt; には行えない。もちろん Python ランタイムでエラーが出るわけではないが、整数型リストに浮動小数を append すると、もはやそれは整数型のリストではなくなる、という意味で不正な処理となる。&lt;&#x2F;p&gt;
&lt;p&gt;よって、冒頭の例は、&quot;&lt;code&gt;list[int]&lt;&#x2F;code&gt; は &lt;code&gt;list[float]&lt;&#x2F;code&gt; の subtype ではないのでエラーが出る&quot;ということになる。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;generic-type-to-variance&quot;&gt;Generic type と variance&lt;&#x2F;h2&gt;
&lt;p&gt;この話は generic type とその variance という概念と関係がある。&lt;&#x2F;p&gt;
&lt;p&gt;PEP 483では、型を引数にとって別の具体型を返すもののことを generic type と定義している。これは難しい話ではなく、例えば &lt;code&gt;list&lt;&#x2F;code&gt; は &lt;code&gt;int&lt;&#x2F;code&gt; を取って、&lt;code&gt;list[int]&lt;&#x2F;code&gt; を返すので generic type である。他にも &lt;code&gt;dict&lt;&#x2F;code&gt; や &lt;code&gt;tuple&lt;&#x2F;code&gt;, &lt;code&gt;Union&lt;&#x2F;code&gt; なども馴染みのある generic type だと思う。&lt;&#x2F;p&gt;
&lt;p&gt;型 &lt;code&gt;t2&lt;&#x2F;code&gt; が型 &lt;code&gt;t1&lt;&#x2F;code&gt; の subtype であるとし、また &lt;code&gt;GenType&lt;&#x2F;code&gt; を好きな generic type であるとしよう。PEP 483 では、&lt;code&gt;GenType[t1]&lt;&#x2F;code&gt; と &lt;code&gt;GenType[t2]&lt;&#x2F;code&gt; の subtype 関係によって、generic type を以下のように分類している&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Covariant, if &lt;code&gt;GenType[t2]&lt;&#x2F;code&gt; is a subtype of &lt;code&gt;GenType[t1]&lt;&#x2F;code&gt; for all such &lt;code&gt;t1&lt;&#x2F;code&gt; and &lt;code&gt;t2&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Contravariant, if &lt;code&gt;GenType[t1]&lt;&#x2F;code&gt; is a subtype of &lt;code&gt;GenType[t2]&lt;&#x2F;code&gt; for all such &lt;code&gt;t1&lt;&#x2F;code&gt; and &lt;code&gt;t2&lt;&#x2F;code&gt;.&lt;&#x2F;li&gt;
&lt;li&gt;Invariant, if neither of the above is true.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;これを型の variance と呼ぶ。Covariant なら &lt;code&gt;t1&lt;&#x2F;code&gt;, &lt;code&gt;t2&lt;&#x2F;code&gt; と同じ subtype 関係を、contravariant なら逆の subtype 関係を持ち、そのような綺麗な関係がない場合は invariant となる。&lt;&#x2F;p&gt;
&lt;p&gt;この用語を使うと冒頭のリストのエラー例の原因は、&quot;&lt;code&gt;list&lt;&#x2F;code&gt; は invariant だから&quot;と表現することもできる。これが mypy のメッセージ、 &lt;code&gt;&quot;List&quot; is invariant&lt;&#x2F;code&gt; だ。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;mou-tou-nowen-ti-nojie-jue-fa&quot;&gt;冒頭の問題の解決法&lt;&#x2F;h2&gt;
&lt;p&gt;Python のリストが invariant である根本原因はリストがミュータブルであることである。要素の置換や追加ができてしまうので、subtype の二つ目の条件が満たせない。&lt;&#x2F;p&gt;
&lt;p&gt;冒頭の例では、個別の要素を操作しようとはしていないので、イミュータブルな配列としてアノテートすれば十分である。そのような用途として使える型に（mypy が suggest している）&lt;code&gt;collections.abc&lt;&#x2F;code&gt; の &lt;code&gt;Sequence&lt;&#x2F;code&gt; という型がある&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; 以下は mypy のエラーが出ない&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;x&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Sequence&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;float&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;y&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Sequence&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; y&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;この辺の事情は &lt;code&gt;dict&lt;&#x2F;code&gt; のような他のミュータブルなコンテナ型でも同様で、要素の型の親子関係をそのまま引き継ぐことはできない（invariant である）。&lt;code&gt;dict&lt;&#x2F;code&gt; のイミュータブル版が必要なら、&lt;code&gt;Mapping&lt;&#x2F;code&gt; を使う。この手の情報はどこを参照すれば良いのかあまりわかっていないが、&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.python.org&#x2F;3&#x2F;library&#x2F;collections.abc.html#collections-abstract-base-classes&quot;&gt;collections.abc のドキュメント&lt;&#x2F;a&gt;を見ると、コンテナ型の抽象基底クラスが列挙されている。&lt;&#x2F;p&gt;
&lt;p&gt;Python は duck typing の精神を持つ言語なので、関数の引数には具体型をつけるより、所望のインターフェースを持つ抽象基底クラスを使う方が良いのだろう。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#practice&quot;&gt;4&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;また、&lt;code&gt;Sequence&lt;&#x2F;code&gt; ではなく、あくまでリストとして扱いたければ、参照ではなくコピーを渡したり&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;x&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; list&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;float&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;y&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; list&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; list&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;y&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;x&lt;&#x2F;code&gt; を &lt;code&gt;Union&lt;&#x2F;code&gt; で表したり&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;python&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;x&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; Union&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span&gt;list&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;float&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; list&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1.0&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;y&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;span&gt; list&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;int&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&lt;&#x2F;span&gt;&lt;span&gt; y&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;という手段も考えられる（後者の例が subtype の条件を満たしていることはすぐ確認できる）。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;omake&quot;&gt;おまけ&lt;&#x2F;h2&gt;
&lt;p&gt;Subtype や variance は Wikipedia にもページがあるくらいなので、Python に限った話ではなく、コンピュータ・サイエンス一般の概念なのだろう。
例えば Julia でも&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;julia&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;julia&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Int&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; Real&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;であるが（Julia では構造体の親子関係の判定に &lt;code&gt;&amp;lt;:&lt;&#x2F;code&gt; というオペレーターが使える）&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;julia&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;julia&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Vector&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;Int&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; Vector{Real}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;false&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;である。これはまさに &lt;code&gt;Vector&lt;&#x2F;code&gt; が invariant だということだ。&lt;&#x2F;p&gt;
&lt;p&gt;ちなみに Julia では &lt;code&gt;Real&lt;&#x2F;code&gt; の子を要素に持つようなベクトルの集合を &lt;code&gt;Vector{&amp;lt;:Real}&lt;&#x2F;code&gt; と書ける。そのため以下は true になる。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;julia&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;julia&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; Vector&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;Int&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; Vector{&amp;lt;:Real}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-constant&quot;&gt;true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;おそらく Python でいう &lt;code&gt;Union[list[int], list[float]]&lt;&#x2F;code&gt; のようなものを表現しているのだろう。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;mypy-online&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;mypy-play.net&quot;&gt;https:&#x2F;&#x2F;mypy-play.net&lt;&#x2F;a&gt; でブラウザから試せる。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;not-runtime&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;もちろんこれは mypy のような static type checker が出すエラーの話であって、冒頭のコードは普通の Python のコードとしては何も問題なく動作する。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;int-float&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;という言い方は多分不正確で、&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;stackoverflow.com&#x2F;questions&#x2F;59619778&#x2F;mypy-why-is-int-a-subtype-of-float&quot;&gt;stack overflow&lt;&#x2F;a&gt; で指摘されているように、&lt;code&gt;float&lt;&#x2F;code&gt; が持つ、&lt;code&gt;is_integer()&lt;&#x2F;code&gt; などを &lt;code&gt;int&lt;&#x2F;code&gt; は持っていない。そのため、上の定義に照らすと、&lt;code&gt;int&lt;&#x2F;code&gt; は &lt;code&gt;float&lt;&#x2F;code&gt; の subtype ではない。実際、&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;peps.python.org&#x2F;pep-0484&#x2F;#the-numeric-tower&quot;&gt;PEP 484 - The numeric tower&lt;&#x2F;a&gt; では &lt;code&gt;float&lt;&#x2F;code&gt; とアノテーションがついたオブジェクトには、&lt;code&gt;int&lt;&#x2F;code&gt; が代入できるよう &quot;shortcut&quot; を導入しようと言っている。同じことが Mypy では &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;mypy.readthedocs.io&#x2F;en&#x2F;stable&#x2F;duck_type_compatibility.html&quot;&gt;duck type compatibility&lt;&#x2F;a&gt; として説明されている。その一方で、PEP 483 では、説明のしやすさのためなのか、&lt;code&gt;int&lt;&#x2F;code&gt; は &lt;code&gt;float&lt;&#x2F;code&gt; の subtype であるかのような書き振りになっている。ここでもそれにならって、あまり厳密にならず、&lt;code&gt;int&lt;&#x2F;code&gt; は &lt;code&gt;float&lt;&#x2F;code&gt; の subtype であるとして話を進めていこうと思う。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;practice&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;4&lt;&#x2F;sup&gt;
&lt;p&gt;実際 PEP 483 には関数の type annotation について &quot;choose the most general type for every argument, and the most specific type for the return value.&quot; とある&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>Rust 製の静的サイトジェネレータ Zola を使ってみる</title>
		<published>2023-01-07T00:00:00+00:00</published>
		<updated>2023-01-07T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2023/zola/"/>
		<id>https://yng87.page/blog/2023/zola/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2023/zola/">&lt;p&gt;年末年始にこのブログの SSG を &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;&quot;&gt;Zola&lt;&#x2F;a&gt; に移行してみた。&lt;&#x2F;p&gt;
&lt;p&gt;Zola は Rust 製の SSG で、シングルバイナリなので依存関係の管理が不要だったり、ビルドが高速であったりということを売りにしているよう。&lt;&#x2F;p&gt;
&lt;p&gt;このブログは今まで Julia 製の SSG である &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;tlienart&#x2F;Franklin.jl&quot;&gt;Franklin.jl&lt;&#x2F;a&gt; で作っていた。Franklin の一番のメリットは数式の記述が楽というところにある。他のツールを使っていると、HTML と LaTeX の文法が競合して思った通りに表示されず、原因を探して適宜エスケープを追加するというような泥臭いプロセスが付き物だった。しかし、Franklin のパーサーはよくできていて、&lt;code&gt;.tex&lt;&#x2F;code&gt; ファイルに書いたような数式をそのまま移行してくることができる。数式中心のシンプルな文章を書く SSG としては優れていると思う。&lt;&#x2F;p&gt;
&lt;p&gt;一方で色々触っていると Franklin ではブログらしい機能を実装するのがなかなか難しいということもわかってきた。例えば pagination や &quot;read more&quot; リンク・多言語対応などを追加してみたかったが、どうすれば良いかわからなかった。きっとできるんじゃないかとは思うが、ドキュメントや例が見つからず、かと言って自分で実装を読むにはちょっと重い。&lt;&#x2F;p&gt;
&lt;p&gt;そう言う事情もあって別の SSG を使ってみようと思って Zola を試してみたらなかなか使い勝手が良かったので、メモを残しておこうと思う。&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;
&lt;h2 id=&quot;zola-noinsutoru&quot;&gt;Zola のインストール&lt;&#x2F;h2&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;documentation&#x2F;getting-started&#x2F;installation&#x2F;&quot;&gt;ドキュメント&lt;&#x2F;a&gt; にあるように、バイナリが配布されているので適当に落としてくる。Mac であれば &lt;code&gt;brew install zola&lt;&#x2F;code&gt; で入る。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;ji-ben-de-nashi-ifang&quot;&gt;基本的な使い方&lt;&#x2F;h2&gt;
&lt;p&gt;基本的な使い方はドキュメントの &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;documentation&#x2F;getting-started&#x2F;overview&#x2F;&quot;&gt;overview&lt;&#x2F;a&gt; を見てほしい。Zola の標準的なディレクトリ構成を引用しておく。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├── config.toml&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├── content&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│   └── blog&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│       ├── _index.md&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│       ├── first.md&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│       └── second.md&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├── sass&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├── static&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├── templates&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│   ├── base.html&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│   ├── blog-page.html&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│   ├── blog.html&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│   └── index.html&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;└── themes&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;section-to-page&quot;&gt;Section と page&lt;&#x2F;h3&gt;
&lt;p&gt;Zola の基本的な構成要素として、section と page というものがある。おおむね、&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;section: 記事を一覧にしてまとめるページ&lt;&#x2F;li&gt;
&lt;li&gt;page: 個別記事のページ&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;というような理解で良いと思う。&lt;&#x2F;p&gt;
&lt;p&gt;コンテンツは &lt;code&gt;&#x2F;content&#x2F;&lt;&#x2F;code&gt; 以下に配置する。上に書いた例では blog という section があり、section ページの設定が &lt;code&gt;&#x2F;content&#x2F;blog&#x2F;_index.md&lt;&#x2F;code&gt;、個別の page 内容が &lt;code&gt;&#x2F;content&#x2F;blog&#x2F;first.md&lt;&#x2F;code&gt; などに入っている。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;tenpureto&quot;&gt;テンプレート&lt;&#x2F;h3&gt;
&lt;p&gt;ブログのデザインは &lt;code&gt;&#x2F;templates&#x2F;&lt;&#x2F;code&gt; 以下の html ファイルと sass で指定する。これらの html では、&lt;code&gt;config.toml&lt;&#x2F;code&gt; などで定義されたパラメータが使用でき、Rust 製のテンプレートエンジンである &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;Keats&#x2F;tera&quot;&gt;Tera&lt;&#x2F;a&gt; で処理される。この辺はドキュメントがあまりまとまっていないため、何か実装したい機能があれば&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;themes&#x2F;&quot;&gt;公開されているテーマ&lt;&#x2F;a&gt;から似たようなものを探して実装を見てみるのが手っ取り早いと思う。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;can-kao-ji-shi&quot;&gt;参考記事&lt;&#x2F;h3&gt;
&lt;p&gt;ここではこれ以上 Zola の細かい説明はしないが、公式のドキュメント以外にも、以下のブログ記事が参考になったので貼っておく&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;another.maple4ever.net&#x2F;archives&#x2F;2833&#x2F;&quot;&gt;Rust 製スタティックサイトジェネレーター Zola をつかう | hiromasa.another :o)&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;konnashi-dousuru&quot;&gt;こんな時どうする？&lt;&#x2F;h2&gt;
&lt;p&gt;ここでは自分が必要になった個別の機能の実装方法について、備忘録も兼ねて紹介する。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;pagination-woru-retai&quot;&gt;Pagination を入れたい&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.getzola.org&#x2F;documentation&#x2F;templates&#x2F;pagination&#x2F;&quot;&gt;ドキュメント&lt;&#x2F;a&gt; に pagination のやり方が書いてあるが、少し不親切だと思う。&lt;&#x2F;p&gt;
&lt;p&gt;Paginate したいセクションがある場合は、まず &lt;code&gt;_index.md&lt;&#x2F;code&gt; ファイルの &lt;code&gt;paginate_by&lt;&#x2F;code&gt; 変数を正の値にする。例えばブログ記事を日付順に、ページあたり10記事表示したいなら、&lt;code&gt;&#x2F;content&#x2F;blog&#x2F;_index.md&lt;&#x2F;code&gt; の front matter を以下のようにする。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;markdown&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+++&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;sort_by = &amp;quot;date&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;paginate_by = 10&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+++&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;これでテンプレート内で &lt;code&gt;paginator&lt;&#x2F;code&gt; が使えるようになる。例えばトップページに記事へのリンクを貼るには &lt;code&gt;&#x2F;templates&#x2F;index.html&lt;&#x2F;code&gt; に以下を追加すれば良い。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{% for page in paginator.pages %}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;li&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;{{ page.permalink | safe }}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;{{ page.title }}&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;li&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{% endfor %}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;yan-yu-gotonirinkuxian-woqie-riti-etai&quot;&gt;言語ごとにリンク先を切り替えたい&lt;&#x2F;h3&gt;
&lt;p&gt;日本語ページを見ているときに、&quot;About&quot; をクリックすると、日本語の About ページへ、英語で見ているときは英語の About へ移動するようにしたい。そのような場合は、&lt;code&gt;get_url&lt;&#x2F;code&gt; に &lt;code&gt;lang&lt;&#x2F;code&gt; パラメータを与える。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;div&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;site_title&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;div&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;menu&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;      &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;{{ get_url(path=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt;@&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt;&#x2F;about&#x2F;_index.md&amp;quot;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; lang&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;lang)&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; }}&lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;{{ trans(key=&amp;quot;about&amp;quot;, lang=lang) }}&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;sekusiyonnodeirekutorigou-zao-nijie-ceng-woshe-ketai&quot;&gt;セクションのディレクトリ構造に階層を設けたい&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;code&gt;&#x2F;content&#x2F;&lt;&#x2F;code&gt; に以下のような階層を設けたい場合、&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;├── content&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│   └── blog&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│       ├── 2022&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│       │    ├── _index.md&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│       │    ├── first.md&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│       │    └── second.md&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│       ├── 2023&#x2F;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│       │    ├── _index.md&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│       │    ├── first.md&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│       │    └── second.md&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;│       ├── index.md&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;子階層の &lt;code&gt;&#x2F;content&#x2F;2022&#x2F;_index.md&lt;&#x2F;code&gt; などに &lt;code&gt;transparent=true&lt;&#x2F;code&gt; を設定する。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;markdown&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+++&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;transparent = true&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;+++&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;ridairekutowoshe-ding-sitai&quot;&gt;リダイレクトを設定したい&lt;&#x2F;h3&gt;
&lt;p&gt;他のブログサービスや SSG から乗り換える場合、パスの構造が変わってしまうのでリダイレクトを設定する必要がある。これはページの &lt;code&gt;aliases&lt;&#x2F;code&gt; で設定可能&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;toml&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt;+++&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;title&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;タイトル&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-variable z-other&quot;&gt;aliases&lt;&#x2F;span&gt;&lt;span&gt; =&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;old&#x2F;path&#x2F;to&#x2F;this&#x2F;article&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;&#x2F;old&#x2F;path&#x2F;to&#x2F;this&#x2F;article&#x2F;2&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt;+++&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;jiao-zhu-biao-shi-nobaguwozhi-sitai&quot;&gt;脚注表示のバグを直したい&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;getzola&#x2F;zola&#x2F;issues&#x2F;1070&quot;&gt;この issue&lt;&#x2F;a&gt; に上がっている通り、現在 footnote 機能にいくつかバグがある。まだ完全には解決されていないようで、issue 内では javascript を使った一時的な workaround が紹介されている。&lt;&#x2F;p&gt;
&lt;p&gt;Javascript ファイルを &lt;code&gt;static&#x2F;footnote.js&lt;&#x2F;code&gt; などとして保存し、テンプレート html に &lt;code&gt;&amp;lt;script type=&quot;text&#x2F;javascript&quot; src=&quot;&#x2F;footnote.js&quot;&amp;gt;&amp;lt;&#x2F;script&amp;gt;&lt;&#x2F;code&gt; を追加すると良い。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;mu-ci-wohao-kinawei-zhi-nibiao-shi-sitai&quot;&gt;目次を好きな位置に表示したい&lt;&#x2F;h3&gt;
&lt;p&gt;Zola 標準の機能で生成できる目次は、ページ内の好きな位置に挿入することができない。ちょっと面倒だが、以下で紹介されているマクロを用いる&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;zola.discourse.group&#x2F;t&#x2F;choose-table-of-content-position&#x2F;506&#x2F;4&quot;&gt;Choose table of content position - Zola&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;このブログでは少し修正して&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;html&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{%- macro toc(toc, level, depth) %}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{%- if level == 1 %}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;div&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;toc&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;Table of contents&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;h3&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{%- endif %}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; class&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;h{{ level }}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{%- for h in toc %}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;li&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        &amp;lt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;a&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity&quot;&gt; href&lt;&#x2F;span&gt;&lt;span&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;{{ h.permalink | safe }}&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;{{ h.title }}&lt;&#x2F;span&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;a&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        {% if h.children and level &lt;&#x2F;span&gt;&lt;span class=&quot;z-invalid z-illegal&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt; depth -%}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;            {{ self::toc(toc=h.children, level=level+1, depth=depth, heading=false) }}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        {%- endif %}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    &amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;li&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    {%- endfor %}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{%- if level != 1 %}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;ul&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{%- endif %}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{%- if level == 1 %}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;&amp;lt;&#x2F;&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name z-tag&quot;&gt;div&lt;&#x2F;span&gt;&lt;span&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{%- endif %}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{%- endmacro %}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{# =================== #}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{# === replace toc === #}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{# =================== #}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{%- macro format_content(resource) %}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; {%- set content = resource.content %}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; {%- if content is containing(&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&amp;lt;!--&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; toc &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;--&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;) %}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;  {%- set content = content | replace(from=&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;&amp;lt;!--&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; toc &lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;--&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;, to=self::toc(toc=resource.toc, level=1, depth=resource.extra.toc_depth | default(value=1))) %}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; {%- endif -%}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt; {{ content | safe }}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;{%- endmacro %}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;という形で使っている。これでマークダウン中に &lt;code&gt;&amp;lt;!-- toc --&amp;gt;&lt;&#x2F;code&gt; と書くと、その部分に目次を挿入してくれる。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;gha-dekasutamudomeinwoshi-tutedepuroisitai&quot;&gt;GHA でカスタムドメインを使ってデプロイしたい&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;shalzz&#x2F;zola-deploy-action&#x2F;blob&#x2F;master&#x2F;README.md#custom-domain&quot;&gt;README&lt;&#x2F;a&gt; にこっそり書いてあるように、&lt;code&gt;&#x2F;static&#x2F;CNAME&lt;&#x2F;code&gt; というテキストファイルを作ってドメイン名を書いておくと、自動で処理してくれる。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;matome&quot;&gt;まとめ&lt;&#x2F;h2&gt;
&lt;p&gt;Zola で一通りブログを書き換えてみたが、求めている機能が十分あり、ドキュメントや example を漁ればそんなに詰まることなく実装できて今のところ満足している。Franklin を使っているときは、毎回 Julia のランタイムを起動する必要があったりするのも地味に面倒だったが、バイナリを一つインストールすれば使えるというのもシンプルで良い。&lt;&#x2F;p&gt;
&lt;p&gt;一方数式の記述については Mathjax を普通に使うだけになったので、Franklin 時代に比べ多少面倒になった。以前書いた記事の数式も結構書き換える必要があった。&lt;&#x2F;p&gt;
&lt;p&gt;今後しばらくは使い倒していきたい。&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>Julia&#x2F;Lux.jl で拡散モデルを実装する</title>
		<published>2022-12-14T00:00:00+00:00</published>
		<updated>2022-12-14T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2022/lux-ddim/"/>
		<id>https://yng87.page/blog/2022/lux-ddim/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2022/lux-ddim/">&lt;p&gt;この記事は &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;qiita.com&#x2F;advent-calendar&#x2F;2022&#x2F;julia&quot;&gt;JuliaLang Advent Calendar 2022&lt;&#x2F;a&gt; 14日目の記事である。&lt;&#x2F;p&gt;
&lt;p&gt;最近趣味で、&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;avik-pal&#x2F;Lux.jl&quot;&gt;Lux.jl&lt;&#x2F;a&gt; という Julia 製のニューラルネットのフレームワークを使って拡散モデルを実装していた。&lt;&#x2F;p&gt;



  



  



  


&lt;div class=&quot;github-card&quot;&gt;
  &lt;a class=&quot;github-card__link&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;github.com&amp;#x2F;yng87&amp;#x2F;DDIM.jl&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;
    &lt;div class=&quot;github-card__badge&quot; aria-hidden=&quot;true&quot;&gt;
      &lt;svg xmlns=&quot;http:&#x2F;&#x2F;www.w3.org&#x2F;2000&#x2F;svg&quot; viewBox=&quot;0 0 24 24&quot; width=&quot;20&quot; height=&quot;20&quot;&gt;&lt;path d=&quot;M5.88401 18.6531C5.58404 18.4523 5.32587 18.1972 5.0239 17.8366C4.91473 17.7062 4.47283 17.1521 4.55811 17.258C4.09533 16.6831 3.80296 16.4168 3.50156 16.3087C2.9817 16.1223 2.7114 15.5497 2.89784 15.0298C3.08428 14.51 3.65685 14.2397 4.17672 14.4261C4.92936 14.696 5.43847 15.1609 6.12425 16.0141C6.03025 15.8972 6.46364 16.4408 6.55731 16.5526C6.74784 16.7802 6.88732 16.918 6.99629 16.9909C7.20118 17.128 7.58451 17.1871 8.14709 17.1308C8.17065 16.7487 8.24136 16.378 8.34919 16.0355C5.38097 15.3101 3.70116 13.3949 3.70116 9.63947C3.70116 8.4006 4.0704 7.28368 4.75917 6.34756C4.5415 5.45368 4.57433 4.37259 5.06092 3.15611C5.1725 2.87715 5.40361 2.66314 5.69031 2.57328C5.77242 2.54949 5.81791 2.5389 5.89878 2.52648C6.70167 2.40319 7.83573 2.69681 9.31449 3.62311C10.181 3.41855 11.0885 3.31476 12.0012 3.31476C12.9129 3.31476 13.8196 3.41835 14.6854 3.62253C16.1619 2.68976 17.2986 2.39625 18.1072 2.52627C18.1919 2.53988 18.2645 2.55758 18.3249 2.57741C18.6059 2.66967 18.8316 2.88155 18.9414 3.15611C19.4279 4.37232 19.4608 5.45319 19.2433 6.34695C19.9342 7.28313 20.3012 8.39184 20.3012 9.63947C20.3012 13.3966 18.627 15.3046 15.6588 16.0318C15.7837 16.4467 15.8496 16.9103 15.8496 17.4118C15.8496 18.0763 15.8471 18.7108 15.8424 19.4223C15.8412 19.6124 15.8397 19.8156 15.8375 20.1279C16.2129 20.2107 16.5229 20.5074 16.6031 20.9086C16.7114 21.4502 16.3602 21.977 15.8186 22.0853C14.6794 22.3132 13.8353 21.5535 13.8353 20.5608C13.8353 20.4705 13.836 20.3414 13.8375 20.1142C13.8398 19.8012 13.8412 19.5987 13.8425 19.4092C13.8471 18.7017 13.8496 18.0714 13.8496 17.4118C13.8496 16.7145 13.6664 16.26 13.4237 16.0508C12.7627 15.481 13.0977 14.3971 13.965 14.2996C16.9314 13.9663 18.3012 12.8174 18.3012 9.63947C18.3012 8.68484 17.9893 7.89547 17.3881 7.23534C17.1301 6.95209 17.0567 6.54634 17.199 6.19062C17.3647 5.77639 17.4354 5.2336 17.2941 4.57678L17.2847 4.57944C16.7928 4.71861 16.1744 5.01956 15.4261 5.52826C15.182 5.69413 14.8772 5.74377 14.5932 5.66388C13.7729 5.43319 12.8913 5.31476 12.0012 5.31476C11.111 5.31476 10.2294 5.43319 9.40916 5.66388C9.12662 5.74335 8.82344 5.69468 8.57997 5.53077C7.8274 5.02414 7.2056 4.72355 6.71079 4.58352C6.56735 5.23672 6.63814 5.77758 6.80336 6.19062C6.94565 6.54634 6.87219 6.95209 6.61423 7.23534C6.01715 7.89096 5.70116 8.69352 5.70116 9.63947C5.70116 12.8114 7.07225 13.9681 10.023 14.2996C10.8883 14.3969 11.2246 15.4767 10.5675 16.048C10.3751 16.2153 10.1384 16.7799 10.1384 17.4118V20.5608C10.1384 21.5472 9.30356 22.2866 8.17878 22.0898C7.63476 21.9946 7.27093 21.4764 7.36613 20.9324C7.43827 20.5201 7.75331 20.2114 8.13841 20.1274V19.1379C7.22829 19.1991 6.47656 19.0496 5.88401 18.6531Z&quot; fill=&quot;currentColor&quot;&gt;&lt;&#x2F;path&gt;&lt;&#x2F;svg&gt;
    &lt;&#x2F;div&gt;
    &lt;div class=&quot;github-card__content&quot;&gt;
      &lt;div class=&quot;github-card__repo&quot;&gt;yng87&amp;#x2F;DDIM.jl&lt;&#x2F;div&gt;
      
      &lt;div class=&quot;github-card__description&quot;&gt;Lux.jl implementation of Denoising Diffusion Implicit Models.&lt;&#x2F;div&gt;
      
      
      &lt;div class=&quot;github-card__meta&quot;&gt;Julia&lt;&#x2F;div&gt;
      
    &lt;&#x2F;div&gt;
  &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;
&lt;p&gt;学習データには &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.robots.ox.ac.uk&#x2F;~vgg&#x2F;data&#x2F;flowers&#x2F;102&#x2F;&quot;&gt;Oxford 102 flowers dataset&lt;&#x2F;a&gt; という花のデータセットを使っていて、うまく学習できると以下のような画像が生成できる&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;blog&#x2F;2022&#x2F;ddim_img_005.gif&quot; alt=&quot;DDIM で生成した花の画像&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;使える計算リソースのせいであまり解像度の高いものは作れないが、それでもそれっぽい画像は生成できるようになった。&lt;&#x2F;p&gt;
&lt;p&gt;それで作ったものを適当に twitter に貼ったり、（@MathSorcerer さんの勧めで）&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;discourse.julialang.org&#x2F;t&#x2F;lux-jl-implementation-of-denoising-diffusion-model&#x2F;90275&quot;&gt;Julia discourse に投げたり&lt;&#x2F;a&gt;していたら、最終的に &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;avik-pal&#x2F;Lux.jl&#x2F;tree&#x2F;main&#x2F;examples&#x2F;DDIM&quot;&gt;Lux.jl の公式 example&lt;&#x2F;a&gt; にコミットすることになり、先日無事マージされた。&lt;&#x2F;p&gt;
&lt;p&gt;Julia は個人的に良い言語だとずっと思っているのだけど、機械学習分野では python エコシステムの圧倒的な物量の前にいまいち目立たない存在となっている。それでも最近は、GPU を使った深層学習までできるくらいには色々揃ってきているよう。&lt;&#x2F;p&gt;
&lt;p&gt;今回は拡散モデルという題材で、 Lux.jl やその周辺のエコシステムを使って深層学習モデルを実装する方法を紹介したい。Julia の深層学習関連の記事は日本語でもいくつかあるが、自分でレイヤーをカスタマイズしないといけないようなそれなりに複雑なネットワークを構築している例は少ない。この記事が実践的な深層学習モデルを実装する例として参考になれば良いと思う。&lt;&#x2F;p&gt;
&lt;p&gt;Disclaimer だが、自分の Julia の経験は、博士課程の研究で使った＋趣味で時々触っている程度なので、内容に間違いがあったらコメントしてほしい。あと、タイトルに拡散モデルと書きながら、拡散モデルについての話題はほとんど出てこないのも許してほしい。&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;
&lt;h2 id=&quot;kuo-san-moderunituite&quot;&gt;拡散モデルについて&lt;&#x2F;h2&gt;
&lt;p&gt;拡散モデルは目下大流行中の深層生成モデルで、ランダムな入力から少しずつノイズを除去することで高品質の画像を得ることができる。ググってもらえれば英語でも日本語でも記事はたくさんヒットすると思うので、ここで詳しくは説明しない。一応&lt;a href=&quot;&#x2F;blog&#x2F;2022&#x2F;06&#x2F;diffusion_model_derivation&#x2F;&quot;&gt;自分が書いた記事&lt;&#x2F;a&gt;もある。&lt;&#x2F;p&gt;
&lt;p&gt;今回は &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;keras.io&#x2F;examples&#x2F;generative&#x2F;ddim&#x2F;&quot;&gt;Keras の example&lt;&#x2F;a&gt; を Julia で再実装した。&lt;&#x2F;p&gt;
&lt;p&gt;ここで扱っているのは &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2010.02502&quot;&gt;Denoising Diffusion Implicit Models (DDIM)&lt;&#x2F;a&gt; というもので、それ以前の DDPM などの拡散モデルからの生成をより高速に行えるように改良したものになっている。&lt;&#x2F;p&gt;
&lt;p&gt;また Jax での実装（&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;daigo0927&#x2F;jax-ddim&quot;&gt;daigo0927&#x2F;jax-ddim&lt;&#x2F;a&gt;）もあったので参考にしている。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;julia-noshen-ceng-xue-xi-guan-lian-nopatukezi&quot;&gt;Julia の深層学習関連のパッケージ&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;flux-to-lux&quot;&gt;Flux と Lux&lt;&#x2F;h3&gt;
&lt;p&gt;Julia にはいくつかニューラルネットワーク用のパッケージが存在するが、中でも最も有名なのは &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;FluxML&#x2F;Flux.jl&quot;&gt;Flux.jl&lt;&#x2F;a&gt; だろう。これは立ち位置としては TensorFlow や PyTorch に近い。これらが、モデルをクラスで定義するのと同様に、Flux では NN レイヤーを trainable パラメータを持った構造体で定義する。またデータローダーや activation function も搭載している（実際には NNlib.jl や MLUtils.jl を re-export している）。&lt;&#x2F;p&gt;
&lt;p&gt;一方今回扱う Lux.jl は python でいうと &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;deepmind&#x2F;dm-haiku&quot;&gt;Jax&#x2F;Haiku&lt;&#x2F;a&gt; などのフレームワークに近い。Lux が提供するのはレイヤーの管理機能のみであり、その他の機能は外部のパッケージと連携する必要がある。また、Lux でのレイヤー構造体はモデルの構造を決めるための情報（例えば入力と出力の次元数）だけを保持しており、内部に trainable なパラメータを持たない。&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;lux.csail.mit.edu&#x2F;dev&#x2F;&quot;&gt;Lux のドキュメント&lt;&#x2F;a&gt;には&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Functional Design – Pure Functions and Deterministic Function Calls.&lt;&#x2F;li&gt;
&lt;li&gt;No more implicit parameterization.&lt;&#x2F;li&gt;
&lt;li&gt;Compiler and AD-friendly Neural Networks&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;とあり、Jax 周りのフレームワークと同様に pure function を志向していることがわかる。&lt;&#x2F;p&gt;
&lt;p&gt;このような設計を採用した理由についてはドキュメントの &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;lux.csail.mit.edu&#x2F;dev&#x2F;introduction&#x2F;overview&#x2F;#why-use-lux-over-flux&quot;&gt;Why use Lux over Flux?&lt;&#x2F;a&gt; によくまとまっていて、かいつまんでまとめると、パラメーターを explicit に扱う方が（Julia の機械学習界隈で盛んな）Neural ODE などとの相性が良くなるかららしい。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shen-ceng-xue-xi-noekosisutemu&quot;&gt;深層学習のエコシステム&lt;&#x2F;h3&gt;
&lt;p&gt;Julia のエコシステムには composability &lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#compbug&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; と呼ばれる設計思想のようなものがあって、独立した小さなパッケージをユーザーが自由に組み合わせて開発することが良しとされている。上で書いたように Lux.jl 単独では深層学習はできず、データローダーであったりオプティマイザであったり、はたまた画像や言語処理用の機能は別のパッケージを利用して実装する必要がある。この辺りは、TensorFlow や PyTorch とは対照的であると思う。&lt;&#x2F;p&gt;
&lt;p&gt;ということで、ここでは Julia でニューラルネットを実装するときによく使うパッケージをリストアップする&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;FluxML&#x2F;Zygote.jl&quot;&gt;Zygote.jl&lt;&#x2F;a&gt;: Backward の自動微分のデファクトスタンダード。TensorFlow や PyTorch ではそれぞれ独自の tensor 型を定義してその上で微分を行うが、Zygote は Julia の標準の配列などを使ってそのまま微分ができるのが特徴。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;FluxML&#x2F;NNlib.jl&quot;&gt;NNlib.jl&lt;&#x2F;a&gt;: Activation function などが実装されている。先述のように Zygote を使えば自分で好きに定義した関数で微分ができるのだが、同じ関数でも実装方法によって numerical stability に差が出ることもあるので、NNlib に存在するものは積極的に利用した方が良い。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;JuliaML&#x2F;MLUtils.jl&quot;&gt;MLUtils.jl&lt;&#x2F;a&gt;: データローダーなどの utility が実装されている。PyTorch のデータローダーのように、スレッド並列でデータを読み込む機能もついている。&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;FluxML&#x2F;Optimisers.jl&quot;&gt;Optimisers.jl&lt;&#x2F;a&gt;: 名前の通りオプティマイザの実装がある。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;h2 id=&quot;lux-jl-deniyurarunetutowakuwoshi-zhuang-suru&quot;&gt;Lux.jl でニューラルネットワークを実装する&lt;&#x2F;h2&gt;
&lt;p&gt;前置きが長くなってしまったが、DDIM の実装を通して、Lux の使い方を見ていこう。なお DDIM やそのバックボーンである UNet などの内容はとても説明しきれないので、上に貼った Keras の example やリポジトリの実装を参考にしてほしい。ここでは Lux を使う上でのキーポイントに絞って記すことにする。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;moderunoding-yi&quot;&gt;モデルの定義&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;shi-zhuang-ji-minoreiyawoli-yong-suru&quot;&gt;実装済みのレイヤーを利用する&lt;&#x2F;h4&gt;
&lt;p&gt;UNet アーキテクチャの部品の一つである &lt;code&gt;residual_block&lt;&#x2F;code&gt; の定義を見てみよう。これは入力と出力の画像のチャネル数をもとに、レイヤーを返す関数である。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;julia&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;function&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; residual_block&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;in_channels&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;Int&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; out_channels&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;Int&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    if&lt;&#x2F;span&gt;&lt;span&gt; in_channels &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;==&lt;&#x2F;span&gt;&lt;span&gt; out_channels&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        first_layer &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; NoOpLayer&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    else&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        first_layer &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; Conv&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; in_channels &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; out_channels&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span&gt; pad&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;SamePad&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; Chain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;first_layer&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;                 SkipConnection&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;Chain&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;BatchNorm&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;out_channels&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span&gt; affine&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; momentum&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0.99&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;                                      Conv&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; out_channels &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; out_channels&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span&gt; stride&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                           pad&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; swish&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;                                      Conv&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; out_channels &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; out_channels&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span&gt; stride&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                           pad&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; +&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;畳み込みやバッチ正規化などよく使うレイヤーは、&lt;code&gt;Conv&lt;&#x2F;code&gt; や &lt;code&gt;BatchNorm&lt;&#x2F;code&gt; として Lux に実装済み。現状実装されているレイヤーは&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;lux.csail.mit.edu&#x2F;stable&#x2F;api&#x2F;layers&#x2F;&quot;&gt;ドキュメント&lt;&#x2F;a&gt;を参照してほしい。&lt;&#x2F;p&gt;
&lt;p&gt;Lux ではレイヤーを直列に繋げる場合、&lt;code&gt;Chain&lt;&#x2F;code&gt; を使う。Keras の &lt;code&gt;Sequential&lt;&#x2F;code&gt; のようなイメージ。また &lt;code&gt;SkipConnection(some_layer, +)&lt;&#x2F;code&gt; で $\boldsymbol x + f(\boldsymbol x)$ 型のスキップコネクションが定義できる。&lt;&#x2F;p&gt;
&lt;p&gt;今回は、入力チャネル数 &lt;code&gt;in_channel&lt;&#x2F;code&gt; と出力チャネル数 &lt;code&gt;out_channel&lt;&#x2F;code&gt; が異なる場合のみ、チャネル数を揃えるための畳み込み層を追加しているが、このような分岐は &lt;code&gt;if&lt;&#x2F;code&gt; と &lt;code&gt;NoOpLayer&lt;&#x2F;code&gt; を &lt;code&gt;Chain&lt;&#x2F;code&gt; と組み合わせて実現できる。&lt;&#x2F;p&gt;
&lt;p&gt;Keras の functional API や PyTorch-like なモデルの定義をするには、以下で説明するようなカスタムレイヤーを実装する必要がある。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;kasutamureiyawoshi-zhuang-suru&quot;&gt;カスタムレイヤーを実装する&lt;&#x2F;h4&gt;
&lt;p&gt;単純な構造のモデルは上のように実装すれば良いが、今回使う UNet はダウンサンプリング・ブロックとアップサンプリング・ブロックとの間でスキップコネクションが発生し、単純に &lt;code&gt;Chain&lt;&#x2F;code&gt; と &lt;code&gt;SkipConnection&lt;&#x2F;code&gt; を組み合わせるだけで実装するのは難しい。また DDIM は入力画像にノイズを付与して UNet を呼び出すというような処理が必要になる。このようなケースでは自前でレイヤーを定義して柔軟な処理を実装する方が良いだろう。今回実装した &lt;code&gt;DenoisingDiffusionImplicitModel&lt;&#x2F;code&gt; もカスタムレイヤーを使用しており、概略を示すと以下のようになる。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;julia&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;struct&lt;&#x2F;span&gt;&lt;span&gt; DenoisingDiffusionImplicitModel&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; AbstractFloat&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;       Lux&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span&gt;AbstractExplicitContainerLayer&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;:unet&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; :batchnorm&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    unet&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;UNet&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    batchnorm&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;BatchNorm&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    min_signal_rate&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;T&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    max_signal_rate&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;T&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; コンストラクタ&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;function&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; DenoisingDiffusionImplicitModel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;image_size&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;Tuple{Int, Int}&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                         channels&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;32&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 64&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 96&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 128&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; block_depth&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                         min_freq&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1.0f0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; max_freq&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1000.0f0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                         embedding_dims&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;32&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; min_signal_rate&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0.02f0&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                         max_signal_rate&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;0.95f0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    unet &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; UNet&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    batchnorm &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; BatchNorm&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;...&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; DenoisingDiffusionImplicitModel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;unet&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; batchnorm&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; min_signal_rate&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                           max_signal_rate&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;#&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; モデル呼び出し&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;function&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span&gt;ddim&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;DenoisingDiffusionImplicitModel{T}&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;x&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;Tuple&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;AbstractArray&lt;&#x2F;span&gt;&lt;span&gt;{&lt;&#x2F;span&gt;&lt;span&gt;T&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 4&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                                             AbstractRNG&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ps&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                                    st&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;NamedTuple&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; where&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                                                           T &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;lt;&lt;&#x2F;span&gt;&lt;span&gt;:&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                                                           AbstractFloat&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    images&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; rng &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; x&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; noises を生成し、images + noises から pred_noises, pred_image を得る&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span&gt;noises&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; images&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; pred_noises&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; pred_images&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; st&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Lux のレイヤーは全て &lt;code&gt;Lux.AbstractExplicitLayer&lt;&#x2F;code&gt; の subtype になる。レイヤーが内部に別のレイヤーを含んでいる場合は、&lt;code&gt;Lux.AbstractExplicitContainerLayer&lt;&#x2F;code&gt; の subtype にする。さらに &lt;code&gt;(:unet, :batchnorm)&lt;&#x2F;code&gt; のようにパラメータを与えることで、モデルパラメータの追跡を容易にできる。この辺の書き方は少し面倒かもしれない。カスタムレイヤーについて詳しくは&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;http:&#x2F;&#x2F;lux.csail.mit.edu&#x2F;stable&#x2F;manual&#x2F;interface&#x2F;#lux-interface&quot;&gt;ドキュメント&lt;&#x2F;a&gt;を参照してほしい。&lt;&#x2F;p&gt;
&lt;p&gt;DDIM 呼び出しの入力となっている &lt;code&gt;ps&lt;&#x2F;code&gt; はモデルの trainable なパラメータ、&lt;code&gt;st&lt;&#x2F;code&gt; はバッチ正規化レイヤーの statitics 情報など、レイヤーの状態を表す trainable でない変数である。Lux のレイヤーはこの例のように、&lt;code&gt;(data, model parameter, model state)&lt;&#x2F;code&gt; の三つ組をレイヤーの入力とし、&lt;code&gt;(result, updated state)&lt;&#x2F;code&gt; を返すように実装する。&lt;code&gt;ps&lt;&#x2F;code&gt; や &lt;code&gt;st&lt;&#x2F;code&gt; の初期化についてはこの後説明する。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;rosuguan-shu&quot;&gt;ロス関数&lt;&#x2F;h3&gt;
&lt;p&gt;上で述べたように、Zygote は好きな Julia の関数を微分してくれるので、モデルの出力を使ってロスを好きに定義する。ここではモデル &lt;code&gt;ddim&lt;&#x2F;code&gt; と共にパラメータ &lt;code&gt;ps&lt;&#x2F;code&gt; と状態 &lt;code&gt;st&lt;&#x2F;code&gt; を明示的に受け取る次のような関数を定義した。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;julia&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;function&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; compute_loss&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;ddim&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;DenoisingDiffusionImplicitModel{T}&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; images&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;AbstractArray{T, 4}&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                      rng&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;AbstractRNG&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ps&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; st&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;NamedTuple&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; where&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; AbstractFloat&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span&gt;noises&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; images&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; pred_noises&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; pred_images&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; st &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; ddim&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;images&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; rng&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ps&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; st&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    noise_loss &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; mean&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;abs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;pred_noises &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt; noises&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    image_loss &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; mean&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;abs&lt;&#x2F;span&gt;&lt;span&gt;.&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;pred_images &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;-&lt;&#x2F;span&gt;&lt;span&gt; images&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    loss &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; noise_loss &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;+&lt;&#x2F;span&gt;&lt;span&gt; image_loss&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; loss&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; st&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;&lt;h3 id=&quot;xue-xi&quot;&gt;学習&lt;&#x2F;h3&gt;
&lt;h4 id=&quot;detaroda&quot;&gt;データローダー&lt;&#x2F;h4&gt;
&lt;p&gt;データローダーは MLUtils.jl の &lt;code&gt;DataLoader&lt;&#x2F;code&gt; を使うのが便利。以下のようにデータセットと、バッチ化のオプションを渡す。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;julia&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ds &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; ImageDataset&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;dataset_dir&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; x &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; preprocess_image&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;x&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; image_size&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; true&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;data_loader &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; DataLoader&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;ds&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span&gt; batchsize&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;batchsize&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; partial&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;false&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; collate&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                            parallel&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; rng&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;rng&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; shuffle&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;true&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;DataLoader&lt;&#x2F;code&gt; に渡すデータセットは Julia の構造体で、以下の二つのメソッドを備えていればなんでも良い&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;length(ds::ImageDataset)&lt;&#x2F;code&gt;: データセットのサイズ&lt;&#x2F;li&gt;
&lt;li&gt;&lt;code&gt;getindex(ds::ImageDataset, i::Int)&lt;&#x2F;code&gt;: &lt;code&gt;i&lt;&#x2F;code&gt; 番目のデータの取得&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;そのため RAM に乗り切らないデータはディスクから読むというような状況にも対応できる。PyTorch のデータローダーを意識して設計されている気がする。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;chu-qi-hua&quot;&gt;初期化&lt;&#x2F;h4&gt;
&lt;p&gt;モデルの初期化は &lt;code&gt;Lux.setup&lt;&#x2F;code&gt; で、オプティマイザの初期化は &lt;code&gt;Optimisers.setup&lt;&#x2F;code&gt; で行う。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;julia&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;rng &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Random&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;MersenneTwister&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ddim &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; DenoisingDiffusionImplicitModel&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;image_size&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; image_size&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;;&lt;&#x2F;span&gt;&lt;span&gt; channels&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;channels&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                        block_depth&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;block_depth&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; min_freq&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;min_freq&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                        max_freq&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;max_freq&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; embedding_dims&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;embedding_dims&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                        min_signal_rate&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;min_signal_rate&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                        max_signal_rate&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt;max_signal_rate&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;ps&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; st &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Lux&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;rng&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ddim&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; .|&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; gpu&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;opt &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; AdamW&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;learning_rate&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; (&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;9.0f-1&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 9.99f-1&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; weight_decay&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;opt_st &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Optimisers&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;setup&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;opt&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ps&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; |&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; gpu&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;|&amp;gt; gpu&lt;&#x2F;code&gt; でパラメータ達を GPU に転送する。なお GPU が利用可能でないときは &lt;code&gt;gpu&lt;&#x2F;code&gt; は何もせず入力をそのまま返すので、デバイスに合わせて &lt;code&gt;gpu&lt;&#x2F;code&gt;, &lt;code&gt;cpu&lt;&#x2F;code&gt; を書き換える必要はない。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;xue-xi-rupu&quot;&gt;学習ループ&lt;&#x2F;h4&gt;
&lt;p&gt;学習ループの概略は以下のような感じ。&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;julia&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;function&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; train_step&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;ddim&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;DenoisingDiffusionImplicitModel{T}&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; images&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;AbstractArray{T, 4}&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    rng&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;AbstractRNG&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ps&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; st&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;NamedTuple&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                    opt_st&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;::&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;NamedTuple&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; where&lt;&#x2F;span&gt;&lt;span&gt; {&lt;&#x2F;span&gt;&lt;span&gt;T&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; &amp;lt;:&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; AbstractFloat&lt;&#x2F;span&gt;&lt;span&gt;}&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    (&lt;&#x2F;span&gt;&lt;span&gt;loss&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; st&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; back &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Zygote&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;pullback&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;p &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;-&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; compute_loss&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;ddim&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; images&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; rng&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; p&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; st&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ps&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    gs &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; back&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;one&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;loss&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; nothing&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;1&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    opt_st&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ps &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Optimisers&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;update&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;opt_st&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ps&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; gs&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; loss&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ps&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; st&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; opt_st&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;rng_gen &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Random&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;MersenneTwister&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;Random&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;seed!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;rng_gen&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 0&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;for&lt;&#x2F;span&gt;&lt;span&gt; epoch &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;in&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 1&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;:&lt;&#x2F;span&gt;&lt;span&gt;epochs&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    losses &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; [&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    iter &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; ProgressBar&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;data_loader&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    st &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Lux&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;trainmode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;st&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    for&lt;&#x2F;span&gt;&lt;span&gt; images &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;in&lt;&#x2F;span&gt;&lt;span&gt; iter&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        images &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; images &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;|&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; gpu&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;        loss&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ps&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; st&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; opt_st &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; train_step&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;ddim&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; images&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; rng&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; ps&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; st&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; opt_st&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;        push!&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;losses&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; loss&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-support&quot;&gt;        set_description&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;iter&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt; &amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;Epoch: &lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;epoch&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt; Loss: &lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;$&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;mean&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;losses&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-variable&quot;&gt;)&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt;    #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; 各エポックでの推論例の保存&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    st &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; Lux&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;testmode&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;st&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    generated_images&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; _ &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; generate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;ddim&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; Lux&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;replicate&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;rng_gen&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-comment&quot;&gt; #&lt;&#x2F;span&gt;&lt;span class=&quot;z-comment&quot;&gt; to get inference on the same noises&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                    (&lt;&#x2F;span&gt;&lt;span&gt;image_size&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; image_size&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 3&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; 10&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; val_diffusion_steps&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;                                    ps&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; st&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    ...&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;&lt;code&gt;Zygote.pullback&lt;&#x2F;code&gt; が自動微分の部分で、ラムダ関数を使って、モデルパラメータ &lt;code&gt;ps&lt;&#x2F;code&gt; の微分のみを計算できるようにするのがポイント。&lt;&#x2F;p&gt;
&lt;p&gt;今回のモデルはバッチ正規化を使っており、このレイヤーは学習と推論で別の動作をする。この切り替えは &lt;code&gt;Lux.trainmode&#x2F;Lux.testmode&lt;&#x2F;code&gt; で行う。&lt;&#x2F;p&gt;
&lt;p&gt;乱数について注意点がある。今回のモデルではその性質上乱数生成を多用する。例えば学習でも推論でも、&lt;code&gt;randn(rng, ...)&lt;&#x2F;code&gt; のようにしてガウシアンノイズを生成しているが、この &lt;code&gt;randn&lt;&#x2F;code&gt; は乱数シード &lt;code&gt;rng&lt;&#x2F;code&gt; を mutate する。特に推論時には毎回同じ乱数シードについて評価したいので、ここでは &lt;code&gt;Lux.replicate&lt;&#x2F;code&gt; で元の乱数シードのコピーを渡している。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;moderunobao-cun-todu-miip-mi&quot;&gt;モデルの保存と読み込み&lt;&#x2F;h4&gt;
&lt;p&gt;Lux では &lt;code&gt;ps&lt;&#x2F;code&gt;, &lt;code&gt;st&lt;&#x2F;code&gt;, &lt;code&gt;opt_st&lt;&#x2F;code&gt; の三つを与えることでスナップショットが決まる。これらは普通の Julia のオブジェクトなので、適当なライブラリでシリアライズして保存することができる。例えば &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;JuliaIO&#x2F;BSON.jl&quot;&gt;BSON.jl&lt;&#x2F;a&gt; を使うと&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;julia&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;function&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; save_checkpoint&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;ps&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; st&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; opt_st&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; output_dir&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; epoch&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    path &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; joinpath&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;output_dir&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; @sprintf&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span class=&quot;z-string&quot;&gt;checkpoint_%.4d.bson&lt;&#x2F;span&gt;&lt;span class=&quot;z-punctuation z-definition z-string&quot;&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; epoch&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; bson&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;path&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; Dict&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;:ps&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; cpu&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;ps&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; :st&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; cpu&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;st&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt; :opt_st&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt; =&amp;gt;&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt; cpu&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;opt_st&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;となる。これをロードするときも普通に、&lt;&#x2F;p&gt;
&lt;pre class=&quot;giallo z-code&quot;&gt;&lt;code data-lang=&quot;julia&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;function&lt;&#x2F;span&gt;&lt;span class=&quot;z-entity z-name&quot;&gt; load_checkpoint&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;path&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;    d &lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;=&lt;&#x2F;span&gt;&lt;span&gt; BSON&lt;&#x2F;span&gt;&lt;span class=&quot;z-keyword&quot;&gt;.&lt;&#x2F;span&gt;&lt;span class=&quot;z-support&quot;&gt;load&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span&gt;path&lt;&#x2F;span&gt;&lt;span&gt;)&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;    return&lt;&#x2F;span&gt;&lt;span&gt; d&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;:ps&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;:st&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;span&gt;,&lt;&#x2F;span&gt;&lt;span&gt; d&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span class=&quot;z-constant&quot;&gt;:opt_st&lt;&#x2F;span&gt;&lt;span&gt;]&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span class=&quot;z-keyword&quot;&gt;end&lt;&#x2F;span&gt;&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;とすれば良い。&lt;&#x2F;p&gt;
&lt;p&gt;この辺の取り回しの良さは、パラメータや状態を明示的に扱うことの利点だと感じる。&lt;&#x2F;p&gt;
&lt;p&gt;実装全体は冒頭にリンクを貼ったリポジトリを見てほしい。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;matome&quot;&gt;まとめ&lt;&#x2F;h2&gt;
&lt;p&gt;今回の実装を通して、拡散モデルというそれなりに複雑なニューラルネットワークモデルも Julia でそれほど苦もなく実装できることがわかった。Lux は明示的にパラメータを扱うため、自分が何をしているか把握しやすく、見通しが良い。この辺は Jax に通じるものがある。&lt;&#x2F;p&gt;
&lt;p&gt;多くの人が気になるのは python に対して優位性があるのかという点だろう。個人的に Julia で深層学習をするメリットとしては&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;JIT があり、気軽に &lt;code&gt;for&lt;&#x2F;code&gt; ループが書けるので、カスタムで書いたロスやデータの前処理が高速になる。特に numpy でベクトル化することが難しいような処理は Julia を使うと高速化できるはず。&lt;&#x2F;li&gt;
&lt;li&gt;型を付けられるので、型の解析によってバグを減らすことができる。&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;あたりだと感じる。&lt;&#x2F;p&gt;
&lt;p&gt;一方でフレームワークが提供する API を使って簡単に書けるモデルなら TensorFlow や PyTorch で十分だと思うし、プロダクション化のサポートやクラウドサービスとの連携は python の方が圧倒的に強い。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#large&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; 速度に関しても、結局 GPU 上の計算はどちらでもあまり変わらないと思うので、CPU での処理がボトルネックでない限りは Julia が優位ということもない気がする。また、Julia は俗に &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;discourse.julialang.org&#x2F;t&#x2F;taking-ttfx-seriously-can-we-make-common-packages-faster-to-load-and-use&#x2F;74949&quot;&gt;TTFX&lt;&#x2F;a&gt; と呼ばれる JIT のオーバーヘッドが存在するので、コードを書いてスクリプトを実行してデバッグというプロセスに関しては、python の方が体験として良い。&lt;&#x2F;p&gt;
&lt;p&gt;個人的には &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;sciml.ai&#x2F;&quot;&gt;SciML&lt;&#x2F;a&gt; で取り組まれているような、ニューラルネットワークと微分方程式の合わせ技に興味があって、その分野では Julia が優位らしいのでもう少し掘ってみたいと思っている。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;compbug&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;一方でこのせいで色々なパッケージを組み合わせると、各パッケージ開発者が想定していなかったようなバグが生じることがあり、時々批判的なスレッドが生えたりして話題になることがある。例えば&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;news.ycombinator.com&#x2F;item?id=31396861&quot;&gt;これ&lt;&#x2F;a&gt;とか。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;large&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;特にインダストリーでは大規模な分散学習をすることもあると思うが、Julia でそこまで対応できるのかは調べていない。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>ブログに独自ドメインを設定した</title>
		<published>2022-12-02T00:00:00+00:00</published>
		<updated>2022-12-02T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2022/setup-domain/"/>
		<id>https://yng87.page/blog/2022/setup-domain/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2022/setup-domain/">&lt;p&gt;このブログは github pages でデプロイしている。
そのため今まではデフォルトの yng87.github.io というドメイン名だったが、自身に帰属感を持たせ記事執筆のモチベを上げるため、独自ドメインを導入してみた。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;domeinnoqu-de&quot;&gt;ドメインの取得&lt;&#x2F;h2&gt;
&lt;p&gt;ドメインをどこで購入するのが良いのかよく知らないので、まあ Google なら大丈夫だろうと思って、Google Domains を利用した。&lt;&#x2F;p&gt;
&lt;p&gt;購入は非常に簡単だった。ドメイン名は yng87.page を選択（年間約 1200円）。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;github-pages-noshe-ding&quot;&gt;GitHub Pages の設定&lt;&#x2F;h2&gt;
&lt;p&gt;以下のブログを参考にした。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;dev.to&#x2F;trentyang&#x2F;how-to-setup-google-domain-for-github-pages-1p58&quot;&gt;How to setup google domain for github pages&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;手順としては&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;GitHub Pages の settings から custom domain を登録&lt;&#x2F;li&gt;
&lt;li&gt;Google Domain &amp;gt; マイドメイン &amp;gt; DNS &amp;gt; リソース レコード &amp;gt; カスタム レコード で以下を設定する
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;ja&#x2F;pages&#x2F;configuring-a-custom-domain-for-your-github-pages-site&#x2F;managing-a-custom-domain-for-your-github-pages-site#configuring-an-apex-domain&quot;&gt;apex domain を設定&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;docs.github.com&#x2F;ja&#x2F;pages&#x2F;configuring-a-custom-domain-for-your-github-pages-site&#x2F;managing-a-custom-domain-for-your-github-pages-site#configuring-an-apex-domain&quot;&gt;www サブドメインを設定&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;今回取得したドメインは HTTPS を利用する必要があるので、GitHub Pages の settings で Enforce HTTPS にチェックを入れた。諸々のタイムラグがあるようで、GitHub 上で何回か DNS レコードの再確認をする必要があった。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;matome&quot;&gt;まとめ&lt;&#x2F;h2&gt;
&lt;p&gt;無事独自ドメインが設定できたので、もう少し頻繁に記事を書きたい。&lt;&#x2F;p&gt;
&lt;p&gt;そういえば今日 &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;100daystooffload.com&#x2F;&quot;&gt;100 Days To Offload&lt;&#x2F;a&gt; というものがあるのを知ったが、&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Posts don’t need to be long-form, deep, meaningful, or even that well written. If there are spelling and grammar mistakes, or even if there’s no real point to the post, so what?&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;はそうだよねと思った。どうせ誰も見ていない個人ブログなので、もっと気楽に日記や未完成の Tech を書いていきたい。&lt;&#x2F;p&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>CS:APP を読んだ</title>
		<published>2022-06-15T00:00:00+00:00</published>
		<updated>2022-06-15T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2022/book-csapp/"/>
		<id>https://yng87.page/blog/2022/book-csapp/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2022/book-csapp/">&lt;p&gt;Computer Systems: A Programmer&#x27;s Perspective という本を読んだので読書メモ。&lt;&#x2F;p&gt;

  



  



  



  



  



  



  



&lt;div class=&quot;embed-card embed-card--book&quot;&gt;
  &lt;a class=&quot;embed-card__link&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;csapp.cs.cmu.edu&amp;#x2F;3e&amp;#x2F;about.html&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;
    &lt;div class=&quot;embed-card__media&quot;&gt;
      
      &lt;img class=&quot;embed-card__image no-lightense&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;csapp.cs.cmu.edu&amp;#x2F;3e&amp;#x2F;images&amp;#x2F;csapp3e-cover.jpg&quot; alt=&quot;Computer Systems: A Programmer&amp;#x27;s Perspective&quot; loading=&quot;lazy&quot;&gt;
      
    &lt;&#x2F;div&gt;

    &lt;div class=&quot;embed-card__content&quot;&gt;
      
      &lt;div class=&quot;embed-card__label&quot;&gt;Book&lt;&#x2F;div&gt;
      

      &lt;div class=&quot;embed-card__title&quot;&gt;Computer Systems: A Programmer&amp;#x27;s Perspective&lt;&#x2F;div&gt;

      
      &lt;div class=&quot;embed-card__meta&quot;&gt;Randal E. Bryant &amp;#x2F; David R. O&amp;#x27;Hallaron &amp;#x2F; Pearson&lt;&#x2F;div&gt;
      

      

      
    &lt;&#x2F;div&gt;
  &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;

&lt;h2 id=&quot;donnaben&quot;&gt;どんな本?&lt;&#x2F;h2&gt;
&lt;p&gt;プログラムが CPU やメモリをどのように扱うかという、低レイヤーの概観を与えてくれる本。
タイトルに&quot;A Programmer&#x27;s Perspective&quot;とあるように、職業プログラマー向けの本であって、低レイヤーのハードウェア・ソフトウェアを作る専門家向けの本ではない。
プログラムを実行する時に under the hood で何が起きているかという mental model を獲得することで、より速いコードやよりメモリ効率の良いコードを書けるようになるのが目的とのこと。&lt;&#x2F;p&gt;
&lt;p&gt;僕は大学・大学院でずっと物理学ばかりやっていて、コンピュータ・サイエンスをまともに学んでいないのが悩みだったが、この本を読んである程度雰囲気は掴めるようになったと思う。
読んだきっかけは、&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;teachyourselfcs.com&#x2F;&quot;&gt;Teach Yourself Computer Science&lt;&#x2F;a&gt; というサイト。他にも CS の勉強に良さそうな資料が色々挙げられている。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;bi-yao-naqian-ti-zhi-shi&quot;&gt;必要な前提知識&lt;&#x2F;h2&gt;
&lt;p&gt;プログラミング経験は当然必要。また解説用のコードは基本的に C なので、C の読み書きができる必要がある。ポインタとか &lt;code&gt;malloc&lt;&#x2F;code&gt; とかは知っていないと読むのは辛いと思う。ポインタがオブジェクトが格納されているメモリのアドレスだってことは知ってるけど、それって具体的に CPU やメモリはどう扱っているの?というレベルの人&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#me&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; が読むとちょうど良い気がする。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;nei-rong&quot;&gt;内容&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;part-i-program-structure-and-execution&quot;&gt;Part I: Program Structure and Execution&lt;&#x2F;h3&gt;
&lt;p&gt;整数や浮動小数点数の二進法表現を詳細に解説し、x86-64の命令セットについて学ぶ。C のコードをアセンブリ言語に変換して、元のコードと対応づけて読めるようになるということを目指している。また、x86-64に似せた独自の Y86-64というプロセッサーの設計を通して、CPU のパイプライン処理についても勉強する。&lt;&#x2F;p&gt;
&lt;p&gt;僕は今まで、コードをテキストファイルに書いてコンパイルすると謎の仕組みで謎のバイナリが吐き出されるという認識しか持っていなかったが、アセンブリがなんとなく読めるようになることで C のコードがどう変換されてマシン語になるのか雰囲気がわかるようになった。&lt;&#x2F;p&gt;
&lt;p&gt;また、これまでに学んだ知識をベースにしたプログラムの最適化も行う。オーダーの改善ではなく、CPU を最大限効率よく使うことによる $\mathcal O(1)$ の改善をしているのが面白かった。「カリカリにチューンする」というのはこういうことか。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;part-ii-running-programs-on-a-system&quot;&gt;Part II: Running Programs on a System&lt;&#x2F;h3&gt;
&lt;p&gt;リンク・例外・仮想メモリについて学ぶ。例外の章で初めてプロセスという概念が出てくる。&lt;&#x2F;p&gt;
&lt;p&gt;例外の章ではコントロールフローの制御の仕方を学び、ちょっとしたシェルを作成する。普段多用しているシェルという道具の仕組みがわかって楽しかった。&lt;&#x2F;p&gt;
&lt;p&gt;仮想メモリはこの本の集大成の一つで、ハードウェアと OS が協調してメモリを管理していることを学ぶ。
また &lt;code&gt;malloc&lt;&#x2F;code&gt; 等も掘り下げて説明されている。自分は今まで &lt;code&gt;malloc&lt;&#x2F;code&gt; したら要求したメモリ分だけヒープが消費されるみたいな雑な感覚しか持っていなかったが、実際には fragmentation を抑えて効率的にメモリを扱えるように OS が大変な努力をしていることがわかった。実際に簡単なメモリアロケーション・プログラムを作成してちゃんと動くことが確認できるのも面白かった。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;part-iii-interaction-and-communication-between-programs&quot;&gt;Part III: Interaction and Communication between Programs&lt;&#x2F;h3&gt;
&lt;p&gt;Part III ではより進んで、I&#x2F;O・ネットワーク・concurrency について学ぶ。
C で小さい WEB サーバーを作成し、それを multi-process, multiplex, multi-thread それぞれで並行に実行する方法を学ぶ。&lt;&#x2F;p&gt;
&lt;p&gt;特にネットワークに関しては今までどうやって動いているのか全くイメージが持てていなかったので、ソケットというディスクリプタの読み書きをしているということがわかって勉強になった。&lt;&#x2F;p&gt;
&lt;p&gt;この part は Linux のシステムコールを多用するが、それぞれの解説は浅いので、コードを写経して動きをなんとなく理解するという感じになってしまった。深く理解するには別の書籍等が必要だと思う。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;matome&quot;&gt;まとめ&lt;&#x2F;h2&gt;
&lt;p&gt;特に後半は説明が雑になってくるので、それぞれを深く学ぶことはできないが、自分のような非情報系の専攻の人にとっては良い本だと思う。
難点は分量で、（英語で読んだので）1000ページくらいあって、読むのに3ヶ月くらいかかった。しかし、時間をかけただけの価値はあったと思う。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;me&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;この本を読む前の自分のこと&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>Diffusion model を理解するためのノート</title>
		<published>2022-06-14T00:00:00+00:00</published>
		<updated>2022-06-14T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2022/diffusion-model-derivation/"/>
		<id>https://yng87.page/blog/2022/diffusion-model-derivation/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2022/diffusion-model-derivation/">&lt;p&gt;ぼんやりと過ごしているうちに、世間では &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;openai.com&#x2F;dall-e-2&#x2F;&quot;&gt;DALL·E 2&lt;&#x2F;a&gt; や &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;imagen.research.google&#x2F;&quot;&gt;Imagen&lt;&#x2F;a&gt; のような高性能の生成モデルが話題になっていました。いつの間にか生成部分に GAN ではなく、diffusion model というものが使われるようになっていたのも知りました。&lt;&#x2F;p&gt;
&lt;p&gt;このままでは時代に取り残されてしまうと思い diffusion model の草分けと思われる論文の一つ&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2006.11239&quot;&gt;arXiv:2006.11239&lt;&#x2F;a&gt; を理解しようと読み始めたのですが、式の導出が思ったより大変だったので過程をまとめておきたい、というのがこの記事の趣旨です。&lt;&#x2F;p&gt;
&lt;p&gt;目標は &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2006.11239&quot;&gt;arXiv:2006.11239&lt;&#x2F;a&gt; で提案された diffusion model の損失関数&lt;&#x2F;p&gt;
&lt;p&gt;$$\begin{aligned}
L_{\mathrm{simple}}(\theta) = \mathbb{E}_{t,\boldsymbol{x}_0,\boldsymbol{\epsilon}}
\left[\left|
\boldsymbol{\epsilon} - \boldsymbol{\epsilon}_\theta(\sqrt{\bar\alpha_t} \boldsymbol{x}_0 + \sqrt{1-\bar\alpha_t}\boldsymbol{\epsilon},t)
\right|^2\right]
\end{aligned}$$&lt;&#x2F;p&gt;
&lt;p&gt;を導出することです。&lt;&#x2F;p&gt;
&lt;p&gt;Diffusion model とは？というところや、具体的な式の導出については以下のブログにも詳しく書かれています。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;lilianweng.github.io&#x2F;posts&#x2F;2021-07-11-diffusion-models&#x2F;&quot;&gt;What are Diffusion Models? | Lil&#x27;Log&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;zenn.dev&#x2F;nakky&#x2F;articles&#x2F;09fb1804001ff8&quot;&gt;日本語訳もされている&lt;&#x2F;a&gt;ので、そもそも diffusion model って何？という人はそちらを見ると良いと思います。&lt;&#x2F;p&gt;
&lt;p&gt;また上記論文でも appendix や引用文献を詳しく読めばわかるようにはなっています。以下では、それらを読む中で多少ギャップを感じた部分を埋めたり、期待値の定義が曖昧に書かれている部分を explicit にしたりということをしていきます。&lt;&#x2F;p&gt;
&lt;!-- toc --&gt;
&lt;h2 id=&quot;wen-ti-nosetutoatupu&quot;&gt;問題のセットアップ&lt;&#x2F;h2&gt;
&lt;p&gt;$\boldsymbol{x}_0$ をデータ、$\boldsymbol{x}_T$ は $p(\boldsymbol{x}_T) = \mathcal{N}(\boldsymbol{x}_T; \boldsymbol{0},\mathbf{I})$ から生成される正規乱数とします。学習時にはデータ $\boldsymbol{x}_0$ に少しずつノイズを加えて $\boldsymbol{x}_T$ が得られるようにモデルを学習し (&lt;em&gt;diffusion process&lt;&#x2F;em&gt;) 、推論時には逆に $\boldsymbol{x}_T$ を少しずつ denoize することで (&lt;em&gt;reverse process&lt;&#x2F;em&gt;) 学習データ分布からの生成を実現します。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;diffusion-process&quot;&gt;Diffusion process&lt;&#x2F;h3&gt;
&lt;p&gt;Diffusion process は以下のようなマルコフ過程とします
$$\begin{aligned}
\boldsymbol x_0 &amp;amp;\sim q(\boldsymbol x_0),\notag \\
\boldsymbol x_t &amp;amp;\sim q(\boldsymbol x_t | \boldsymbol x_{t-1}) = \mathcal{N}(\boldsymbol x_t;\sqrt{1-\beta_t}\boldsymbol x_{t-1},\beta_t)\quad (t=1,2,\dots,T).
\end{aligned}$$
元々のデータ $\boldsymbol x_0$ を生成する分布を $q(\boldsymbol x_0)$ としています。時刻 $t=0$ から初めて $t=1,2 \dots,T$ で少しずつノイズを加えていく様子を記述しています。$\beta_t$ は $t$ に対して単調増加な関数に選んで、徐々に加えるノイズが大きくなり、より分布の中心がゼロに近づくようにしています。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#beta&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;h3 id=&quot;reverse-process&quot;&gt;Reverse process&lt;&#x2F;h3&gt;
&lt;p&gt;Reverse process は以下のようなマルコフ過程とします
$$\begin{aligned}
\boldsymbol x_T &amp;amp;\sim p(\boldsymbol x_T) = \mathcal{N}(\boldsymbol{x}_T; \boldsymbol{0},\mathbf{I}),\notag\\
\boldsymbol x_{t-1} &amp;amp;\sim p_\theta(\boldsymbol x_{t-1} | \boldsymbol x_t) = \mathcal{N}(\boldsymbol x_{t-1};\boldsymbol\mu_\theta(\boldsymbol x_t, t), \boldsymbol\Sigma_\theta(\boldsymbol x_t, t))\quad (t=T-1,\dots,0) .\tag{1}
\end{aligned}$$
ここで $\theta$ は学習パラメータです。Reverse process は正規乱数 $\boldsymbol x_T$ から始まり、これからノイズを除去して、学習データ分布からのサンプルに近づくように学習します。&lt;&#x2F;p&gt;
&lt;p&gt;学習では以下の negative log likelihood を最小にします&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#KL&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;
$$\begin{aligned}
\tag{2}
\mathbb{E}_{\boldsymbol x_0}[-\log p_\theta(\boldsymbol x_0)]
&amp;amp;= -\int d\boldsymbol x_0 q(\boldsymbol x_0)\log\left[\int d\boldsymbol x_{1:T} p_\theta(\boldsymbol x_{0:T})\right],\\
\mathrm{where}\quad p_\theta(\boldsymbol x_{0:T}) &amp;amp;= p(\boldsymbol x_T)\prod_{t=1}^Tp_\theta(\boldsymbol x_{t-1} | \boldsymbol x_t).\notag
\end{aligned}$$
データ $\boldsymbol x_0$ の確率ができるだけ高くなるように、$\theta$ を調整することになります。$\boldsymbol x_0 \sim q(\boldsymbol x_0)$ としたので、期待値は $q(\boldsymbol x_0)$ に関する積分になります。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;variational-bound-wodao-chu-suru&quot;&gt;Variational bound を導出する&lt;&#x2F;h2&gt;
&lt;p&gt;式 (2) を学習に便利なように変形していきます。&lt;&#x2F;p&gt;
&lt;p&gt;Jensen の不等式から&lt;&#x2F;p&gt;
&lt;p&gt;$$\begin{aligned}
-\log p_\theta(\boldsymbol x_0)
&amp;amp;=
-\log\left[\int d\boldsymbol x_{1:T} p_\theta(\boldsymbol x_{0:T})\right]\notag\\
&amp;amp;=
-\log\left[\int d\boldsymbol x_{1:T} q(\boldsymbol x_{1:T}|\boldsymbol x_0)
\frac{p_\theta(\boldsymbol x_{0:T})}{q(\boldsymbol x_{1:T}|\boldsymbol x_0)}\right]\notag\\
&amp;amp;\leq
-\int d\boldsymbol x_{1:T} q(\boldsymbol x_{1:T}|\boldsymbol x_0)\log\left[\frac{p_\theta(\boldsymbol x_{0:T})}{q(\boldsymbol x_{1:T}|\boldsymbol x_0)}\right].
\end{aligned}$$&lt;&#x2F;p&gt;
&lt;p&gt;なので&lt;&#x2F;p&gt;
&lt;p&gt;$$\begin{aligned}
\mathbb{E}_{\boldsymbol x_0}[-\log p_\theta(\boldsymbol x_0)]
&amp;amp;\leq
-\int d\boldsymbol x_{0:T} q(\boldsymbol x_{0:T})\log\left[\frac{p_\theta(\boldsymbol x_{0:T})}{q(\boldsymbol x_{1:T}|\boldsymbol x_0)}\right]\notag\\
&amp;amp;=\int d\boldsymbol x_{0:T} q(\boldsymbol x_{0:T})
\left[-\log p(\boldsymbol x_T) - \sum_{t=1}^T\log\frac{p_\theta(\boldsymbol x_{t-1}|\boldsymbol x_t)}{q(\boldsymbol x_t|\boldsymbol x_{t-1})}\right]\notag\\
&amp;amp;= \mathbb{E}_q\left[-\log p(\boldsymbol x_T) - \sum_{t=1}^T\log\frac{p_\theta(\boldsymbol x_{t-1}|\boldsymbol x_t)}{q(\boldsymbol x_t|\boldsymbol x_{t-1})}\right]\notag\\
&amp;amp;=:L.
\end{aligned}$$&lt;&#x2F;p&gt;
&lt;p&gt;$\mathbb{E}_q$ は $q(\boldsymbol x_{0:T})$ の下での平均としました。変分法でよくある式変形だと思います。以降は式 (2) の上限である $L$ を最小化することにします。&lt;&#x2F;p&gt;
&lt;p&gt;天下りですが、$q(\boldsymbol x_t|\boldsymbol x_{t-1})$ を以下のように分解します
$$\begin{aligned}
q(\boldsymbol x_t|\boldsymbol x_{t-1})
=q(\boldsymbol x_t|\boldsymbol x_{t-1}, \boldsymbol x_0)
=q(\boldsymbol x_{t-1}|\boldsymbol x_t, \boldsymbol x_0)\frac{q(\boldsymbol x_t|\boldsymbol x_0)}{q(\boldsymbol x_{t-1}|\boldsymbol x_0)}.
\end{aligned}$$&lt;&#x2F;p&gt;
&lt;p&gt;これを $L$ の中の和の $t&amp;gt;1$ の部分に適用すると&lt;&#x2F;p&gt;
&lt;p&gt;$$\begin{aligned}
L
&amp;amp;=
\mathbb{E}_q\left[
-\log p(\boldsymbol x_T) - \sum_{t=2}^T\log\frac{p_\theta(\boldsymbol x_{t-1}|\boldsymbol x_t)}{q(\boldsymbol x_{t-1}|\boldsymbol x_t, \boldsymbol x_0)}
+ \sum_{t=2}^T\log\frac{q(\boldsymbol x_t|\boldsymbol x_0)}{q(\boldsymbol x_{t-1}|\boldsymbol x_0)}
- \log\frac{p_\theta(\boldsymbol x_0|\boldsymbol x_1)}{q(\boldsymbol x_1|\boldsymbol x_0)}
\right]\notag\\
&amp;amp;=
\mathbb{E}_q\left[
-\log\frac{p(\boldsymbol x_T)}{q(\boldsymbol x_T|\boldsymbol x_0)}
- \sum_{t=2}^T\log\frac{p_\theta(\boldsymbol x_{t-1}|\boldsymbol x_t)}{q(\boldsymbol x_{t-1}|\boldsymbol x_t, \boldsymbol x_0)}
- \log p_\theta(\boldsymbol x_0|\boldsymbol x_1)
\right]\notag\\
&amp;amp;\equiv L_T + \sum_{t=2}^T L_{t-1} + L_0.
\end{aligned}$$
以下で見るように、この形にすることで $L$ を tractable な形で書くことができるようになります。&lt;&#x2F;p&gt;
&lt;p&gt;なお、この式の第一項 $L_T$ は学習パラメータを含まないため、考える必要はありません。以下では第二項、第三項を見ていきます。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;l-t-1-noji-suan&quot;&gt;$L_{t-1}$ の計算&lt;&#x2F;h3&gt;
&lt;p&gt;$L_{t-1}$ を計算するには、$q(\boldsymbol x_t|\boldsymbol x_0)$ と $q(\boldsymbol x_{t-1}|\boldsymbol x_t, \boldsymbol x_0)$ の関数形を求める必要があります。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;q-boldsymbol-x-t-boldsymbol-x-0-noji-suan&quot;&gt;$q(\boldsymbol x_t|\boldsymbol x_0)$ の計算&lt;&#x2F;h4&gt;
&lt;p&gt;任意の時刻 $t$ での diffusion の分布 $q(\boldsymbol x_t|\boldsymbol x_0)$ は、単なる Gaussian noise の合成なので、解析的に計算できます&lt;&#x2F;p&gt;
&lt;p&gt;$$\begin{aligned}
q(\boldsymbol x_t|\boldsymbol x_0)
&amp;amp;=
\int d\boldsymbol x_{1:t-1}\prod_{s=1}^{t}q(\boldsymbol x_s|\boldsymbol x_{s-1})\notag\\
&amp;amp;=
\int d\boldsymbol x_{1:t-1}\prod_{s=1}^{t} \frac{1}{\sqrt{2\pi}\beta_s}
\exp\left[-\frac{1}{2\beta_s}\left|\boldsymbol x_s - \sqrt{1-\beta_s}\boldsymbol x_{s-1}\right|^2\right].
\end{aligned}$$
まず $\boldsymbol x_{t-1}$ による積分を考えます。関連する項だけ抜き出すと&lt;&#x2F;p&gt;
&lt;p&gt;$$\begin{aligned}
\int d\boldsymbol x_{t-1}
\exp\left[
-\frac{1}{2(1-\alpha_t)}\left|\boldsymbol x_t - \sqrt{\alpha_t}\boldsymbol x_{t-1}\right|^2
-\frac{1}{2(1-\alpha_{t-1})}\left|\boldsymbol x_{t-1} - \sqrt{\alpha_{t-1}}\boldsymbol x_{t-2}\right|^2
\right]
\end{aligned}$$
となります。論文にならって $\alpha_t = 1-\beta_t$ としました。$\boldsymbol x_{t-1} = \sqrt{\alpha_{t-1}}\boldsymbol x_{t-2} + \sqrt{1-\alpha_{t-1}}\boldsymbol z_{t-1}$ と変数変換し $\boldsymbol z_{t-1}$ について平方完成して積分すると
$$\begin{aligned}
&amp;amp;\int d\boldsymbol z_{t-1}
\exp\left[
-\frac{1}{2(1-\alpha_t)}\left|\sqrt{\alpha_t(1-\alpha_{t-1})}\boldsymbol z_{t-1} - (\boldsymbol x_t - \sqrt{\alpha_t\alpha_{t-1}}\boldsymbol x_{t-2})\right|^2
-\frac{1}{2}\boldsymbol z_{t-1}^2
\right]\notag\\
&amp;amp;\propto
\exp\left(-\frac{1}{2(1-\alpha_t\alpha_{t-1})}|\boldsymbol x_t - \sqrt{\alpha_t\alpha_{t-1}}\boldsymbol x_{t-2}|^2\right)
\end{aligned}$$
となるので、これを繰り返すと
$$\begin{aligned}
q(\boldsymbol x_t|\boldsymbol x_0)
&amp;amp;\propto
\exp\left(-\frac{1}{2(1-\bar\alpha_t)}|\boldsymbol x_t - \sqrt{\bar\alpha_t}\boldsymbol x_0|^2\right),
\mathrm{where}\quad \bar\alpha_t = \prod_{s=1}^t\alpha_t
\end{aligned}$$
つまり
$$\begin{aligned}
\tag{3}
q(\boldsymbol x_t|\boldsymbol x_0)
=\mathcal{N}(\boldsymbol x_t; \sqrt{\bar\alpha_t}\boldsymbol x_0, (1-\bar\alpha_t)\mathbf{I})
\end{aligned}$$
となることがわかります。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;q-boldsymbol-x-t-1-boldsymbol-x-t-boldsymbol-x-0-noji-suan&quot;&gt;$q(\boldsymbol x_{t-1}|\boldsymbol x_t,\boldsymbol x_0)$ の計算&lt;&#x2F;h4&gt;
&lt;p&gt;式 (3) を使うことで
$$\begin{aligned}
q(\boldsymbol x_{t-1}|\boldsymbol x_t,\boldsymbol x_0)
&amp;amp;=\frac{q(\boldsymbol x_t|\boldsymbol x_{t-1})q(\boldsymbol x_{t-1}|\boldsymbol x_0)}{q(\boldsymbol x_t|\boldsymbol x_0)} \notag\\
&amp;amp;\propto
\exp\left[
-\frac{1}{2}\left(
\frac{1}{1-\alpha_t}|\boldsymbol x_t - \sqrt{\alpha_t}\boldsymbol x_{t-1}|^2
+\frac{1}{1-\bar\alpha_{t-1}}|\boldsymbol x_{t-1} - \sqrt{\bar\alpha_{t-1}}\boldsymbol x_0|^2
\right)
\right]\notag\\
&amp;amp;\propto
\exp\left[
-\frac{1}{2}\left(
\frac{1-\bar\alpha_t}{(1-\alpha_t)(1-\bar\alpha_{t-1})}\boldsymbol x_{t-1}^2
-2\left(\frac{\sqrt{\alpha_t}}{1-\alpha_t}\boldsymbol x_t + \frac{\sqrt{\bar\alpha_{t-1}}}{1-\bar\alpha_{t-1}}\boldsymbol x_0\right)\boldsymbol x_{t-1}
\right)
\right],
\end{aligned}$$
となります。$\boldsymbol x_{t-1}$ について二次なので Gaussian です。
$q(\boldsymbol x_{t-1}|\boldsymbol x_t,\boldsymbol x_0) = \mathcal{N}(\boldsymbol x_{t-1}|\tilde{\boldsymbol\mu}_t(\boldsymbol x_t, \boldsymbol x_0), \tilde\beta_t\mathbf{I})$ とおくと&lt;&#x2F;p&gt;
&lt;p&gt;$$\begin{aligned}
\tilde{\boldsymbol\mu}_t(\boldsymbol x_t, \boldsymbol x_0)
&amp;amp;=\frac{(1-\alpha_t)(1-\bar\alpha_{t-1})}{1-\bar\alpha_t}
\left(
\frac{\sqrt{\alpha_t}}{1-\alpha_t}\boldsymbol x_t + \frac{\sqrt{\bar\alpha_{t-1}}}{1-\bar\alpha_{t-1}}\boldsymbol x_0
\right)\notag\\
&amp;amp;=
\frac{(1-\alpha_t)\sqrt{\bar\alpha_{t-1}}}{1-\bar\alpha_t}\boldsymbol x_0
+\frac{(1-\bar\alpha_{t-1})\sqrt\alpha_t}{1-\bar\alpha_t}\boldsymbol x_t\notag\\
\tilde\beta_t
&amp;amp;=\frac{(1-\alpha_t)(1-\bar\alpha_{t-1})}{1-\bar\alpha_t}
\end{aligned}$$
となります。&lt;&#x2F;p&gt;
&lt;h4 id=&quot;variational-bound-noji-suan&quot;&gt;Variational bound の計算&lt;&#x2F;h4&gt;
&lt;p&gt;論文にならって $\Sigma_\theta(\boldsymbol x_t, t) = \sigma^2_t\mathbf{I}$ という ansatz をおくと&lt;&#x2F;p&gt;
&lt;p&gt;$$\begin{aligned}
L_{t-1}
&amp;amp;=-\mathbb{E}_q\left[\log\frac{p_\theta(\boldsymbol x_{t-1}|\boldsymbol x_t)}{q(\boldsymbol x_{t-1}|\boldsymbol x_t, \boldsymbol x_0)}\right]\notag\\
&amp;amp;=-\int d\boldsymbol x_0 d\boldsymbol x_{t-1}d\boldsymbol x_t q(\boldsymbol x_0)q(\boldsymbol x_t|\boldsymbol x_0)q(\boldsymbol x_{t-1}| \boldsymbol x_0, \boldsymbol x_t)
\log p_\theta(\boldsymbol x_{t-1}|\boldsymbol x_t) + \mathrm{const.}\notag\\
&amp;amp;=\int d\boldsymbol x_0 d\boldsymbol x_{t-1}d\boldsymbol x_t q(\boldsymbol x_0)q(\boldsymbol x_t|\boldsymbol x_0)q(\boldsymbol x_{t-1}| \boldsymbol x_0, \boldsymbol x_t)
\frac{1}{2\sigma^2_t}|\boldsymbol x_{t-1} - \boldsymbol\mu_\theta(\boldsymbol x_t, t)|^2 + \mathrm{const.}\notag\\
&amp;amp;=\int d\boldsymbol x_0 d\boldsymbol x_t q(\boldsymbol x_0)q(\boldsymbol x_t|\boldsymbol x_0)
\frac{1}{2\sigma^2_t}|\tilde{\boldsymbol\mu}_t(\boldsymbol x_t, \boldsymbol x_0)  - \boldsymbol\mu_\theta(\boldsymbol x_t, t)|^2 + \mathrm{const.}
\end{aligned}$$
$\mathrm{const.}$ は学習パラメータ $\theta$ を含まないという意味です。$\sigma^2_t$ の関数形は任意性があります。詳しくは元論文を見てください。&lt;&#x2F;p&gt;
&lt;p&gt;この式のままでも良いのですが、実用上は以下のようなリパラメトライゼーションをするとより性能がよくなるそうです。&lt;&#x2F;p&gt;
&lt;p&gt;式 (3) を思い出すと、$\boldsymbol x_t$ は $\boldsymbol\epsilon \sim \mathcal{N}(\boldsymbol\epsilon;\boldsymbol 0,\mathbf{I})$ によって $\boldsymbol x_t=\sqrt{\bar\alpha_t}\boldsymbol x_0 + \sqrt{1-\bar\alpha_t}\boldsymbol\epsilon$ とパラメトライズでき、これにより $\tilde{\boldsymbol\mu}_t = (\boldsymbol x_t - \beta_t&#x2F;\sqrt{1-\bar\alpha_t}\boldsymbol\epsilon) &#x2F; \sqrt{\alpha_t}$ と書けます。よって $\boldsymbol\mu_\theta(\boldsymbol x_t, t)$ も同様に
$$\begin{aligned}
\tag{4}
\boldsymbol\mu_\theta(\boldsymbol x_t, t)
= \frac{1}{\sqrt{\alpha_t}}
\left(
\boldsymbol x_t - \frac{\beta_t}{\sqrt{1-\bar\alpha_t}}\boldsymbol\epsilon_\theta(\boldsymbol x_t, t)
\right)
\end{aligned}$$
とパラメトライズすると&lt;&#x2F;p&gt;
&lt;p&gt;$$\begin{aligned}
L_{t-1}
&amp;amp;=\int d\boldsymbol x_0 d\boldsymbol\epsilon q(\boldsymbol x_0)\mathcal{N}(\boldsymbol\epsilon;\boldsymbol 0,\mathbf{I})
\frac{\beta^2_t}{2\sigma^2_t\alpha_t(1-\bar\alpha_t)}|\boldsymbol\epsilon  - \boldsymbol\epsilon_\theta(\boldsymbol x_t, t)|^2 + \mathrm{const.}\notag\\
&amp;amp;=\mathbb{E}_{\boldsymbol x_0, \boldsymbol\epsilon}
\left[
\frac{\beta^2_t}{2\sigma^2_t\alpha_t(1-\bar\alpha_t)}
\left|\boldsymbol\epsilon  - \boldsymbol\epsilon_\theta(\sqrt{\bar\alpha_t}\boldsymbol x_0 + \sqrt{1-\bar\alpha_t}\boldsymbol\epsilon, t)\right|^2
\right]
+ \mathrm{const.} \tag{5}
\end{aligned}$$&lt;&#x2F;p&gt;
&lt;p&gt;となります。各学習データ $\boldsymbol x_0$ に対して、標準正規分布から乱数 $\boldsymbol\epsilon$ を生成し、モデル $\boldsymbol\epsilon_\theta$ に入力した結果が元の乱数 $\boldsymbol\epsilon$ を再現するように学習することになります。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;l-0-noji-suan&quot;&gt;$L_0$ の計算&lt;&#x2F;h3&gt;
&lt;p&gt;$L_0=\mathbb{E}_q[-\log p_\theta(\boldsymbol x_0|\boldsymbol x_1)]$ は denoizing の最終レイヤーであり、デコーダの役割を果たします。ここだけさらに複雑なモデルを設定することもできるらしいですが、論文では単に independent discrete decoder、つまり $\boldsymbol\mu_\theta(\boldsymbol x_1,1)$ をそのまま $\boldsymbol x_0$ として使うようなデコーダを考えています。&lt;&#x2F;p&gt;
&lt;p&gt;論文では画像データの生成を考えているので各ピクセルは $0, 1, \dots, 255$ の離散値を取り、$x_0$ の各成分はそれを $[-1, 1]$ へ線形にスケールしたものとしています。なので、取りうる値は $-1, -1+2&#x2F;255, -1 + 4&#x2F;255, \dots, 1$ であり、$2&#x2F;255$ ずつになっています。&lt;&#x2F;p&gt;
&lt;p&gt;論文では以下のような likelihood を設定しています&lt;&#x2F;p&gt;
&lt;p&gt;$$\begin{aligned}
\tag{6}
p_\theta(\boldsymbol x_0|\boldsymbol x_1)
= \prod_{i=1}^D\int_{\delta_-(x_0^i)}^{\delta_+(x_0^i)} dx\mathcal{N}(x;\mu_\theta^i(\boldsymbol x_1, 1), \sigma_1^2),
\end{aligned}$$&lt;&#x2F;p&gt;
&lt;p&gt;$$\begin{aligned}
\delta_+(x) =
\begin{cases}
\infty\quad(x = 1)\\
x + 1&#x2F;255\quad(x&amp;lt;1)
\end{cases}\quad
\delta_-(x) =
\begin{cases}
-\infty\quad(x = -1)\\
x - 1&#x2F;255\quad(x&amp;gt;-1)
\end{cases}
\end{aligned}$$&lt;&#x2F;p&gt;
&lt;p&gt;直観的には、データの値 $x_0^i$ の $\pm 1&#x2F;255$ の区間に $\mu_\theta^i$ が入ると likelihood が高くなるということです。また $x_0^i=+1$ $(-1)$ の時は、上 (下) に外す分には構わないという式になっています。&lt;&#x2F;p&gt;
&lt;p&gt;式 (6) のままでは扱いづらいので、$x_0^i=\pm1$ のコーナーケースを無視し、積分を区間の長さ $2&#x2F;255$ が短いと思って近似してみます。すると&lt;&#x2F;p&gt;
&lt;p&gt;$$\begin{aligned}
-\log p_\theta(\boldsymbol x_0|\boldsymbol x_1)
\simeq
\frac{1}{2\sigma_1^2}|\boldsymbol x_0 - \boldsymbol\mu_\theta(\boldsymbol x_1, 1)|^2
+\mathrm{const.}
\end{aligned}$$&lt;&#x2F;p&gt;
&lt;p&gt;と書けるので&lt;&#x2F;p&gt;
&lt;p&gt;$$\begin{aligned}
\tag{7}
L_0 =
\mathbb{E}_{\boldsymbol x_0, \boldsymbol\epsilon}\left[
\frac{1-\alpha_1}{2\sigma_1^2\alpha_1}
\left|\boldsymbol\epsilon - \boldsymbol\epsilon_\theta(\sqrt{\bar\alpha_1}\boldsymbol x_0 + \sqrt{1-\bar\alpha_1}\boldsymbol\epsilon, 1)\right|^2
\right]
+\mathrm{const.}
\end{aligned}$$&lt;&#x2F;p&gt;
&lt;p&gt;となり、$L_{t-1}$ に $t=1$ を代入した結果と一致することがわかります。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;mu-de-guan-shu-nojian-lue-hua&quot;&gt;目的関数の簡略化&lt;&#x2F;h3&gt;
&lt;p&gt;実用上は式 (5) と (7) の二乗誤差の前の係数は無視しても良いらしいです。$t$ についての和も、$[1,T]$ からの一様サンプリングに置き換えることにすると、損失関数は結局&lt;&#x2F;p&gt;
&lt;p&gt;$$\begin{aligned}
\tag{8}
L_{\mathrm{simple}}(\theta)
= \mathbb{E}_{t,\boldsymbol{x}_0,\boldsymbol{\epsilon}}
\left[\left|
\boldsymbol{\epsilon} - \boldsymbol{\epsilon}_\theta(\sqrt{\bar\alpha_t} \boldsymbol{x}_0 + \sqrt{1-\bar\alpha_t}\boldsymbol{\epsilon},t)
\right|^2\right]
\end{aligned}$$&lt;&#x2F;p&gt;
&lt;p&gt;で良くなります。これは各時刻における重みを変えていることになります。&lt;&#x2F;p&gt;
&lt;p&gt;式 (5) のままではノイズの分散が小さい small $t$ における損失の weight が大きくなります。式 (8) のように取り直すことで、より難しい、large $t$ における学習にフォーカスできるようになるらしいです。&lt;&#x2F;p&gt;
&lt;p&gt;実際に学習する際には、まず $\boldsymbol{\epsilon}_\theta$ に使うモデルを決め、データ $\boldsymbol x_0$ と乱数 $t$, $\boldsymbol \epsilon$ を使って、式 (8) を損失関数とした optimize を行います。推論ではまず正規乱数 $\boldsymbol x_T$ をサンプルします。そして学習済みのモデル $\boldsymbol{\epsilon}_\theta$ を使って式 (4) から $\boldsymbol\mu_\theta$ を計算、式 (1) を使って $\boldsymbol x_{t-1}$ をサンプリング、というステップを $t=0$ まで繰り返すことでデータ生成を行います。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;shi-zhuang&quot;&gt;実装&lt;&#x2F;h2&gt;
&lt;p&gt;著者の tensorflow 実装が存在します。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;hojonathanho&#x2F;diffusion&quot;&gt;hojonathanho&#x2F;diffusion: Denoising Diffusion Probabilistic Models&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;また pytorch による再現実装が存在します。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;lucidrains&#x2F;denoising-diffusion-pytorch&quot;&gt;lucidrains&#x2F;denoising-diffusion-pytorch: Implementation of Denoising Diffusion Probabilistic Model in Pytorch&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;実装では、$\boldsymbol\epsilon_\theta$ のモデルとして U-Net が使われています。&lt;&#x2F;p&gt;
&lt;p&gt;Pytorch の方を CIFAR-10で1時間程度動かしてみたのですが、ステップ数が足りず品質の低い画像しか生成されませんでした。&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;github.com&#x2F;lucidrains&#x2F;denoising-diffusion-pytorch&#x2F;issues&#x2F;4&quot;&gt;issue&lt;&#x2F;a&gt; を見ても、この10倍くらいは時間をかける必要がありそうです。Diffusion model は学習に時間がかかる……&lt;&#x2F;p&gt;
&lt;p&gt;&lt;img src=&quot;&#x2F;blog&#x2F;2022&#x2F;diffusion-model-pytorch-9.png&quot; alt=&quot;CIFAR-10 で学習した diffusion model の生成画像&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;colab.research.google.com&#x2F;drive&#x2F;1cxZj_36AzZ3AM3d0sLfuo5Z3pjR532li?usp=sharing&quot;&gt;自分のノートブック&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;h2 id=&quot;matome&quot;&gt;まとめ&lt;&#x2F;h2&gt;
&lt;p&gt;とりあえず目的であった diffusion model の損失関数の導出については理解できました。勉強のためにも自分で再現実装したいところです。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;beta&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;論文ではこれを学習パラメータにすることもできると書かれています。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;KL&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;$q(\boldsymbol x_0)$ と $p_\theta(\boldsymbol x_0)$ の KL divergence を最小にすると言っても良いと思います。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>実務でレコメンドをやっているのでKaggle H&amp;Mコンペに参加しました</title>
		<published>2022-05-14T00:00:00+00:00</published>
		<updated>2022-05-14T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2022/kaggle-h-and-m/"/>
		<id>https://yng87.page/blog/2022/kaggle-h-and-m/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2022/kaggle-h-and-m/">&lt;p&gt;最近 Kaggle で開催されていた、H&amp;amp;M 主催のファッション推薦コンペに参加しました。自分はファッション EC 企業でレコメンドシステムの構築に関わっており、このコンペはまさに自分が扱っている技術・ドメインの問題です。腕試しと技術研鑽のため、参加することにしました。&lt;&#x2F;p&gt;
&lt;p&gt;結果は25位という、なんとも言えない悔しい結果になってしまいましたが (gold が欲しかった)、色々学びがあったので記録を残しておきます。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#excuse&quot;&gt;1&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt;&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.kaggle.com&#x2F;competitions&#x2F;h-and-m-personalized-fashion-recommendations&#x2F;&quot;&gt;H&amp;amp;M Personalized Fashion Recommendations | Kaggle&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;自分の解法は以下の discussion に上げています。&lt;&#x2F;p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.kaggle.com&#x2F;competitions&#x2F;h-and-m-personalized-fashion-recommendations&#x2F;discussion&#x2F;324293&quot;&gt;25th place solution&lt;&#x2F;a&gt;&lt;&#x2F;p&gt;
&lt;p&gt;なお一口に業務のレコメンドと言っても、バッチで1日一回だけ計算をすれば良いのか、オンラインでリアルタイムに推論するのか、組織の ML インフラが整っているのか等によって使う技術や考慮事項は変わってくるはずです。ここで書くことはそのようなレコメンドという広い領域の一部を経験した者からの主観的な感想です。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;konpenogai-yao&quot;&gt;コンペの概要&lt;&#x2F;h2&gt;
&lt;p&gt;詳細は&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.kaggle.com&#x2F;competitions&#x2F;h-and-m-personalized-fashion-recommendations&#x2F;overview&quot;&gt;コンペのページ&lt;&#x2F;a&gt;を見てください。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;detasetuto&quot;&gt;データセット&lt;&#x2F;h3&gt;
&lt;p&gt;H&amp;amp;M から以下のようなデータが提供されました&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;2年分の購買トランザクション&lt;&#x2F;li&gt;
&lt;li&gt;ユーザーのメタデータ&lt;&#x2F;li&gt;
&lt;li&gt;アイテムのメタデータ&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;これをもとに、ユーザーの将来7日間の購買予測をするコンペです。アイテムの情報として、カテゴリや価格のデータだけでなく、画像や商品説明文もあるのが特徴でした。アイテムのユニーク数は約10万、ユーザーのユニーク数は約100万で、比較的規模の大きいデータセットになっていました。&lt;&#x2F;p&gt;
&lt;p&gt;基本的には（画像や説明文を除けば）テーブルデータのタスクなのですが、与えられたデータに正例・負例のラベルがついているわけではありません。あくまでユーザーがいつ何を買ったかというデータがあるだけで、負例は存在しません。いわゆる implicit feedback の問題です。実務ではあるあるの設定なので、経験者はアドバンテージがあったと思います。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shi-wu-nodetatobi-jiao-site&quot;&gt;実務のデータと比較して&lt;&#x2F;h3&gt;
&lt;p&gt;データの形式としては比較的実務に近いと思います。一つ大きな違いはユーザーの行動履歴が購買しかないことでした。通常 EC サイトを運営していれば、購買以外にも様々な行動のログが落ちているはずで、以下のような情報が有用なはずです。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;ユーザーのアイテム閲覧行動（購買行動よりはるかに数が多い）&lt;&#x2F;li&gt;
&lt;li&gt;セッションにおけるアイテム閲覧系列&lt;&#x2F;li&gt;
&lt;li&gt;e.t.c.&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;行動情報が限られているのは&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;H&amp;amp;M には実店舗もあるので、実店舗を中心に使うユーザーと EC サイト中心に行動するユーザーとの扱いが公平になる&lt;&#x2F;li&gt;
&lt;li&gt;そもそも閲覧ログのデータ量は購買より桁で多いので、コンペで使うには大きすぎる&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;という事情があるのだと想像しています。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;jie-fa&quot;&gt;解法&lt;&#x2F;h2&gt;
&lt;h3 id=&quot;2-stage-rekomendo&quot;&gt;2-stage レコメンド&lt;&#x2F;h3&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.kaggle.com&#x2F;competitions&#x2F;h-and-m-personalized-fashion-recommendations&#x2F;discussion&#x2F;307288&quot;&gt;人気 discussion&lt;&#x2F;a&gt;で紹介されていたこともあり、2-stage レコメンドを採用する人が多かったと思います。2-stage レコメンドは、まず粗い方法でアイテムコーパスから少数の関連アイテムを抽出し (1st stage, candidate generation)、その後高精度なモデルで結果を並び替える (2nd stage, reranking) 手法を指します。最初から高精度な 2nd stage モデルでスコアをつけられれば良いのですが、ユーザー・アイテムの数がある程度多くなると、計算量的に厳しくなります。そのような場合によく取られる手法だと思います。自分も同じ方針でモデリングしました。&lt;&#x2F;p&gt;
&lt;p&gt;なお、2-stage レコメンドは多くのテック企業で事例があります。パッと思いつくだけでも&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;static.googleusercontent.com&#x2F;media&#x2F;research.google.com&#x2F;ja&#x2F;&#x2F;pubs&#x2F;archive&#x2F;45530.pdf&quot;&gt;YouTube&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;2105.09293&quot;&gt;twitter&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;arxiv.org&#x2F;abs&#x2F;1702.07969&quot;&gt;Pinterest&lt;&#x2F;a&gt;&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;などがあります。実応用もされているレコメンドの strategy を学ぶという意味で良いコンペだったと思います。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;shang-wei-nojie-fa&quot;&gt;上位の解法&lt;&#x2F;h3&gt;
&lt;p&gt;コンペ終了後上位の解法に目を通しましたが、どれも大枠では似通っていました。&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;1st stage: 質の高い複数の candidate generation をする&lt;&#x2F;li&gt;
&lt;li&gt;2nd stage: GBDT で reranking&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;パッと見る限り、2nd stage に使う特徴量はどのチームも似たような感じでした。ただ &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.kaggle.com&#x2F;competitions&#x2F;h-and-m-personalized-fashion-recommendations&#x2F;discussion&#x2F;324129&quot;&gt;3rd place solution&lt;&#x2F;a&gt; では BPR で作った user2item similarity が有効だったと報告されており、特徴量の作り込みが大事なことには変わりないようです。&lt;&#x2F;p&gt;
&lt;p&gt;スコアに大きな差を生むのは 1st stage の質だったように感じました。ここで色々な可能性を探索して recall が上がるように調整できたかどうかが重要でした。多くの解法に共通しているのは&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;再購入: ユーザーの過去購入と同じアイテム、もしくは同じ product code の商品（色違いなど）を推薦する&lt;&#x2F;li&gt;
&lt;li&gt;アイテムベース協調フィルタリング&lt;&#x2F;li&gt;
&lt;li&gt;人気アイテム&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;です。再購入の重要度が高いのは面白く、ベーシックなアイテムが安価に購入できる H&amp;amp;M ならではの特徴なのかもしれません。&lt;&#x2F;p&gt;
&lt;p&gt;ここでの解法をそのまま実務に応用できるかは組織のインフラによるわけですが、個人的には candidate generation の質の重要性を改めて確認できたという意味で勉強になりました。&lt;&#x2F;p&gt;
&lt;h3 id=&quot;niyurarunetutowakunoyou-xiao-xing&quot;&gt;ニューラルネットワークの有効性&lt;&#x2F;h3&gt;
&lt;p&gt;またニューラルネットワークを使った解法が少なかったことも面白かったです。上位解法では、「NN 試したけど微妙だった」というコメントがいくつかありました。例えば &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.kaggle.com&#x2F;competitions&#x2F;h-and-m-personalized-fashion-recommendations&#x2F;discussion&#x2F;324070&quot;&gt;1st place solution&lt;&#x2F;a&gt; には&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;I tried FM for recall and MLP,transformer for rank,didn&#x27;t work.Luckily I gave up soon to focus on GBDT.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;とあり、また &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;www.kaggle.com&#x2F;competitions&#x2F;h-and-m-personalized-fashion-recommendations&#x2F;discussion&#x2F;324197&quot;&gt;2nd place solution&lt;&#x2F;a&gt; には&lt;&#x2F;p&gt;
&lt;blockquote&gt;
&lt;p&gt;Deep learning models - I tried several recbole models but they weren&#x27;t so good.&lt;&#x2F;p&gt;
&lt;&#x2F;blockquote&gt;
&lt;p&gt;とありました。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#recbole&quot;&gt;2&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; ちなみに自分は時間がなかったこともあり、そもそも NN を深くは検討しませんでした。&lt;&#x2F;p&gt;
&lt;p&gt;一方、産業界ではレコメンドでも NN にシフトしていく動きが多いように思います。例えば最近出ていた &lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;ojs.aaai.org&#x2F;index.php&#x2F;aimagazine&#x2F;article&#x2F;view&#x2F;18140&quot;&gt;Netflix の論文&lt;&#x2F;a&gt; では、彼らがなぜ NN 系の技術を選択するようになったかが説明されています。エコシステムが充実しておりエンジニアリング観点のメリットが大きい、様々なソースからの heterogeneous な特徴量を使うことによる性能向上が大きいという内容です。NN の方が分散学習させやすかったり、ベクトル近傍探索で candidate generation ができたりするので、規模が大きくなるほどメリットも増すように思います。&lt;&#x2F;p&gt;
&lt;h2 id=&quot;matome&quot;&gt;まとめ&lt;&#x2F;h2&gt;
&lt;p&gt;機械学習コンペに初めて参加しましたが、公開ノートブックや discussion によって高速で学べることは素晴らしかったです。明確に順位がつくので自分のスキルを客観視することができました。特に、上位解法を見ると、あの部分をもう少し深掘りしていれば…という悔しい気持ちになります。&lt;&#x2F;p&gt;
&lt;p&gt;一方コンペはあくまでコンペなので、切り分けは当然必要です。特に、実務のレコメンドでは真に改善したいオンラインのメトリクスと、オフラインで試行錯誤して最適化するメトリクスがきちんと相関するかが大きな課題になります。&lt;sup class=&quot;footnote-reference&quot;&gt;&lt;a href=&quot;#KPI&quot;&gt;3&lt;&#x2F;a&gt;&lt;&#x2F;sup&gt; そのためオフラインでの改善だけでなく、高速に AB テストを回してオンラインでの仮説検証をすることも重要なように思います。&lt;&#x2F;p&gt;
&lt;hr &#x2F;&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;excuse&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;1&lt;&#x2F;sup&gt;
&lt;p&gt;Kaggle 初参加で取り組んだ期間も実質一ヶ月だったので、そこそこ健闘したと思いたい。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;recbole&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;2&lt;&#x2F;sup&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow noreferrer external&quot; href=&quot;https:&#x2F;&#x2F;recbole.io&#x2F;&quot;&gt;RecBole&lt;&#x2F;a&gt; はレコメンドアルゴリズムのライブラリの一つ。NN 系が多い。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
&lt;div class=&quot;footnote-definition&quot; id=&quot;KPI&quot;&gt;&lt;sup class=&quot;footnote-definition-label&quot;&gt;3&lt;&#x2F;sup&gt;
&lt;p&gt;例えば Netflix の報告でも、オフラインとオンラインのメトリクスの乖離が課題に挙げられています。&lt;&#x2F;p&gt;
&lt;&#x2F;div&gt;
</content>
	</entry>
	<entry xml:lang="ja">
		<title>t検定で必要なサンプルサイズを求める方法</title>
		<published>2021-10-09T00:00:00+00:00</published>
		<updated>2021-10-09T00:00:00+00:00</updated>
		<link rel="alternate" type="text/html" href="https://yng87.page/blog/2021/t-test-sample-size/"/>
		<id>https://yng87.page/blog/2021/t-test-sample-size/</id>
    
		<content type="html" xml:base="https://yng87.page/blog/2021/t-test-sample-size/">&lt;p&gt;A&#x2F;B テスト実践ガイドという本を読んでいたら、&lt;strong&gt;有意水準5%、検出力80%のt検定に必要なサンプルサイズ&lt;&#x2F;strong&gt;を求める公式として以下のようなものが書かれていました&lt;&#x2F;p&gt;

  



  



  



  



  



  



  



&lt;div class=&quot;embed-card embed-card--book&quot;&gt;
  &lt;a class=&quot;embed-card__link&quot; href=&quot;https:&amp;#x2F;&amp;#x2F;www.kadokawa.co.jp&amp;#x2F;product&amp;#x2F;302101000901&amp;#x2F;&quot; target=&quot;_blank&quot; rel=&quot;noreferrer noopener&quot;&gt;
    &lt;div class=&quot;embed-card__media&quot;&gt;
      
      &lt;img class=&quot;embed-card__image no-lightense&quot; src=&quot;https:&amp;#x2F;&amp;#x2F;cdn.kdkw.jp&amp;#x2F;cover_1000&amp;#x2F;302101&amp;#x2F;302101000901.webp&quot; alt=&quot;A&amp;#x2F;B テスト実践ガイド真のデータドリブンへ至る信用できる実験とは&quot; loading=&quot;lazy&quot;&gt;
      
    &lt;&#x2F;div&gt;

    &lt;div class=&quot;embed-card__content&quot;&gt;
      
      &lt;div class=&quot;embed-card__label&quot;&gt;Book&lt;&#x2F;div&gt;
      

      &lt;div class=&quot;embed-card__title&quot;&gt;A&amp;#x2F;B テスト実践ガイド真のデータドリブンへ至る信用できる実験とは&lt;&#x2F;div&gt;

      
      &lt;div class=&quot;embed-card__meta&quot;&gt;Ron Kohavi &amp;#x2F; Diane Tang &amp;#x2F; Ya Xu &amp;#x2F; 大杉直也訳 &amp;#x2F; KADOKAWA&lt;&#x2F;div&gt;
      

      

      
    &lt;&#x2F;div&gt;
  &lt;&#x2F;a&gt;
&lt;&#x2F;div&gt;

&lt;p&gt;$$n = \frac{16\sigma^2}{\delta^2}.$$&lt;&#x2F;p&gt;
&lt;p&gt;ここで$\sigma$は分散（両群で等しいとする）、 $\delta$ は検出したい二群の平均の差です。この式がどこから出てくるのかパッとわからなかったので考えました。&lt;&#x2F;p&gt;
&lt;p&gt;用語ですが&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;有意水準5%とは、帰無仮説が真である時に帰無仮説を棄却する誤りを犯す確率が5%&lt;&#x2F;li&gt;
&lt;li&gt;検出力80%とは、帰無仮説が偽である時に帰無仮説を棄却できる確率が80%&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;ということです。両者はトレードオフの関係にあります。実験を設定する時には、検出したい差の大きさ $\delta$ を検出できる（差が$\delta$の時に高い確率で帰無仮説を棄却できる）ようなサンプルサイズを確保することが求められます。この辺をちゃんと分析していないと、A&#x2F;B テストをやってみて有意差が出なかったが、それが本当に差がなかったからなのか、単にサンプルサイズが足りなかったからなのかという点が区別できません。なので実験前に期待される差の大きさと分散から必要なサンプルサイズを見積もっておく必要があるわけです。&lt;&#x2F;p&gt;
&lt;p&gt;上の式の話に戻ります。二群の平均の差に関する t 検定なので&lt;&#x2F;p&gt;
&lt;p&gt;$$ t = \frac{\delta}{\sigma}\sqrt{\frac{n}{2}}$$&lt;&#x2F;p&gt;
&lt;p&gt;を考えることになります。サンプル数は両群で等しいとします。有意水準5%で帰無仮説を棄却できないのは $|t|&amp;lt;1.96$ となるときです。
帰無仮説が偽 ($ \delta \cancel= 0 $) のときに得られた $t$ がこの領域に入ってしまうと、「帰無仮説が偽であるときに帰無仮説を棄却しない」誤りを犯すことになります。
検出力80%という意味はこの誤りの確率を20%以下にするということです。標準正規分布の上側20%点は0.84なので、&lt;&#x2F;p&gt;
&lt;p&gt;$$ t = \frac{\delta}{\sigma}\sqrt{\frac{n}{2}} &amp;gt; 1.96 + 0.84 $$&lt;&#x2F;p&gt;
&lt;p&gt;であれば求める検出力を達成できることになります。図にすると以下のような感じです
&lt;img src=&quot;&#x2F;blog&#x2F;2021&#x2F;t_test.png&quot; alt=&quot;t 検定の検出力&quot; &#x2F;&gt;&lt;&#x2F;p&gt;
&lt;p&gt;多くのケースでは検出したい差の大きさ（効果量）$\delta$ を定めて必要な $n$ を計算することになると思います。なので必要なサンプルサイズは最低でも&lt;&#x2F;p&gt;
&lt;p&gt;$$ n = 2(1.96 + 0.84)^2\frac{\sigma^2}{\delta^2} \simeq \frac{16\sigma^2}{\delta^2} $$&lt;&#x2F;p&gt;
&lt;p&gt;となります。&lt;&#x2F;p&gt;
</content>
	</entry>
</feed>
