<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>AI &gt; 評価・実験設計 on M1KE BL0G</title><link>https://www.m1ke.org/categories/ai_eval/</link><description>Recent content in AI &gt; 評価・実験設計 on M1KE BL0G</description><generator>Hugo -- gohugo.io</generator><language>ja-jp</language><copyright>mike</copyright><atom:link href="https://www.m1ke.org/categories/ai_eval/index.xml" rel="self" type="application/rss+xml"/><item><title>フリガナデータをクレンジングする時に直した項目のメモ</title><link>https://www.m1ke.org/p/%E3%83%95%E3%83%AA%E3%82%AC%E3%83%8A%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E3%82%AF%E3%83%AC%E3%83%B3%E3%82%B8%E3%83%B3%E3%82%B0%E3%81%99%E3%82%8B%E6%99%82%E3%81%AB%E7%9B%B4%E3%81%97%E3%81%9F%E9%A0%85%E7%9B%AE%E3%81%AE%E3%83%A1%E3%83%A2/</link><pubDate>Fri, 18 Sep 2026 22:00:00 +0900</pubDate><guid>https://www.m1ke.org/p/%E3%83%95%E3%83%AA%E3%82%AC%E3%83%8A%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E3%82%AF%E3%83%AC%E3%83%B3%E3%82%B8%E3%83%B3%E3%82%B0%E3%81%99%E3%82%8B%E6%99%82%E3%81%AB%E7%9B%B4%E3%81%97%E3%81%9F%E9%A0%85%E7%9B%AE%E3%81%AE%E3%83%A1%E3%83%A2/</guid><description>&lt;img src="https://www.m1ke.org/p/%E3%83%95%E3%83%AA%E3%82%AC%E3%83%8A%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E3%82%AF%E3%83%AC%E3%83%B3%E3%82%B8%E3%83%B3%E3%82%B0%E3%81%99%E3%82%8B%E6%99%82%E3%81%AB%E7%9B%B4%E3%81%97%E3%81%9F%E9%A0%85%E7%9B%AE%E3%81%AE%E3%83%A1%E3%83%A2/f.png" alt="Featured image of post フリガナデータをクレンジングする時に直した項目のメモ" /&gt;&lt;h2 id="背景"&gt;背景&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;日本語のフリガナ（読み）予測モデルを作る過程で、訓練データのバグを数え切れないほど直してきた&lt;/li&gt;
&lt;li&gt;感覚としては百回近く直している気がするが、量が多すぎて自分でも何が起こって何を直したのかを把握しきれなくなっていた&lt;/li&gt;
&lt;li&gt;そこで、実際に手を動かしたプロジェクトの記録を洗い出し、遭遇した問題を体系的に整理し直した&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="データの問題"&gt;データの問題&lt;/h2&gt;
&lt;p&gt;遭遇した問題は、大きく「データそのものの中身の問題」と「それを扱う仕組み側の問題」に分けられる。まずは前者から整理する。&lt;/p&gt;
&lt;h3 id="文字表記レベル"&gt;文字・表記レベル&lt;/h3&gt;
&lt;p&gt;これらは比較的機械的に検出・修正できる問題が多い。&lt;/p&gt;
&lt;h4 id="全角半角の不統一"&gt;全角/半角の不統一&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;数字・記号・英字が全角と半角で混在して記録され、同じ内容が別表記として扱われてしまう&lt;/li&gt;
&lt;li&gt;特に電話番号や日付のような定型的な文字列で起こりやすい&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;０３－１２３４－５６７８
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;03-1234-5678
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="異体字旧字体新字体の不統一"&gt;異体字・旧字体/新字体の不統一&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;同じ文字が複数の字体で記録され、見た目は近くても別のUnicodeコードポイントとして扱われる&lt;/li&gt;
&lt;li&gt;姓名や地名に多く、機械的な正規化だけでは対応しきれないことがある&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;高　（通常の「高」）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;髙　（はしごだかの異体字）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;辺　（新字体）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;邊　（旧字体）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="旧仮名遣い現代仮名遣いの混在"&gt;旧仮名遣い/現代仮名遣いの混在&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;歴史的仮名遣いが現代仮名遣いに正規化されないまま残っている&lt;/li&gt;
&lt;li&gt;古い文献由来のデータで特に目立つ&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;てふてふ　（歴史的仮名遣い）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ちょうちょう　（現代仮名遣い）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="unicode正規化の不備"&gt;Unicode正規化の不備&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;見た目は同じ文字でも、結合文字と合字などUnicode上の内部表現が異なる場合がある&lt;/li&gt;
&lt;li&gt;文字列としての一致判定が正しく行われず、同じ語が別物として扱われる&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ガ　（単一の合字、U+30AC）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ガ　（「カ」＋結合濁点、U+30AB U+3099）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="大字旧漢数字の扱い漏れ"&gt;大字（旧漢数字）の扱い漏れ&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;壱・弐・参のような大字（旧漢数字）が、通常の漢数字と別表記として扱われ、正規化から漏れる&lt;/li&gt;
&lt;li&gt;契約書や領収書のような改竄防止目的の文書に多い&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;壱萬円
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;一万円
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="英字頭字語の表記統一の限界"&gt;英字・頭字語の表記統一の限界&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;頭字語の大文字/小文字やハイフンの有無が統一されていない&lt;/li&gt;
&lt;li&gt;アルファベット部分の読み方自体も語ごとに慣用が異なり、一般化できない&lt;/li&gt;
&lt;li&gt;実世界の使用自体が割れているケースを無理に一つの正解に決めてしまうと、それ自体が新たなノイズになる&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;K-POP → ケーポップ
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;J-WAVE → ジェイウェーブ
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="読み表記レベル"&gt;読み表記レベル&lt;/h3&gt;
&lt;h4 id="カタカナひらがな表記の不統一"&gt;カタカナ/ひらがな表記の不統一&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;読みをカタカナで統一するかひらがなで統一するかが、データソースによって異なる&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ワタシ
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;わたし
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="促音拗音の表記ゆれ"&gt;促音・拗音の表記ゆれ&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;特にOCR由来のデータで、小書きの「っ」「ゃ」などが通常サイズの文字として誤認識されることがある&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;きゃく　（正）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;きやく　（誤認識）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="長音符の表記ゆれ"&gt;長音符の表記ゆれ&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;長音符の表記が二重・三重に壊れて記録されることがある&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;経営 → ケイエイ　（正）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;経営 → ケーエイ／ケイエー　（表記ゆれ）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="濁点半濁点の脱落"&gt;濁点・半濁点の脱落&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;OCR由来のデータで、濁点・半濁点が読み取れず脱落することがある&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;がっこう　（正）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;かつこう　（濁点脱落）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="意味文脈レベル"&gt;意味・文脈レベル&lt;/h3&gt;
&lt;h4 id="多義語同綴異訓語の曖昧性"&gt;多義語・同綴異訓語の曖昧性&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;同じ表記でも文脈によって読みが変わる語が数多く存在する&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;方 → カタ（読み方）／ホウ（方法）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;何時 → ナンジ（何時に行く）／ナンドキ（何時だと思ってる）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;家 → イエ（家を建てる）／ウチ（家に帰る）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="熟字訓と通常の音訓読みの混同"&gt;熟字訓と通常の音訓読みの混同&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;熟語全体で一つの読みが決まる熟字訓が、文字単位の音訓読みと混同され、誤って分解されることがある&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;今日 → キョウ　（熟字訓、正）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;今日 → コン＋ジツ　（文字単位に誤分解）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="固有名詞の読み"&gt;固有名詞の読み&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;人名・地名は、そもそも一意に決まらないことが珍しくない&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;東 → アズマ
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;東 → ヒガシ
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="機械的なルールで決まる読みと判断が必要な読みの混同"&gt;機械的なルールで決まる読みと、判断が必要な読みの混同&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;音韻規則として機械的に決まる読みと、単なる固有名詞混入による統計的パターンを混同すると、誤ったルール化をしてしまう&lt;/li&gt;
&lt;li&gt;頻度だけを根拠に一般化すると、後者を誤ってルール化してしまう&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;◯分間 → 直前の数字に応じて機械的に決まる音韻規則
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;右衛門 → 「良右衛門」という一人の人名が繰り返し出現しているだけの統計的パターン
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="曖昧性そのものの種類分け"&gt;曖昧性そのものの種類分け&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;曖昧に見える語でも原因は一様ではなく、少なくとも3種類に分けて考える必要がある&lt;/li&gt;
&lt;li&gt;この3つを区別しないと、本質的多義型の語に対して「もっとデータを増やせば解決する」という誤った対処をしてしまう&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;候補漏れ型: 正解の読みがそもそも候補集合に含まれていない
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;本質的多義型: 文脈をいくら与えても読みが一意に定まらない（英字読みの慣用差を含む）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;文脈依存型: 「市場」のように、文脈さえ与えられれば一意に定まる
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="分割アライメントレベル"&gt;分割・アライメントレベル&lt;/h3&gt;
&lt;h4 id="形態素解析の構造的な分割融合ミス"&gt;形態素解析の構造的な分割・融合ミス&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;辞書の語彙不足とは別種の、境界（トークン分割）の問題であり、頻繁に発生した&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;何時 → 何 + 時　（意図しない分割）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;二十 → 二 + 十　（複数桁数詞の結合失敗）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="ラベル位置のアライメントの甘さ"&gt;ラベル位置のアライメントの甘さ&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;正解の読みを部分文字列の一致だけで判定すると、対応箇所を取り違えることがある&lt;/li&gt;
&lt;li&gt;「読みそのものが間違っている」バグとは別軸の、「読みは合っているが対応箇所がズレている」バグ&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;今日は今日で忙しい
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;^^^^ 1回目（正解ラベルの本来の対象）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ^^^^ 2回目（部分文字列一致で誤って紐付けられる）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="ソースアノテーション品質レベル"&gt;ソース・アノテーション品質レベル&lt;/h3&gt;
&lt;h4 id="複数の辞書コーパス間での読みの不一致"&gt;複数の辞書・コーパス間での読みの不一致&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;辞書やコーパスによって、同じ語に対して異なる読みが採用されていることがある&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;私 → ワタシ　（辞書A）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;私 → ワタシ／ワタクシ　（辞書B）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="人手アノテーションの入力ミス"&gt;人手アノテーションの入力ミス&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;単純なタイプミスや読み間違いが、そのままラベルとして記録されてしまう&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;よむ　（正）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;よぬ　（入力ミス）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="ソースの信頼度によるgoldsilverの区別漏れ"&gt;ソースの信頼度によるGold/Silverの区別漏れ&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;単純な二値の区分だけでは不十分で、実際には信頼度を段階的なティアに分けて管理する方が扱いやすい&lt;/li&gt;
&lt;li&gt;機械的なクロスチェックを先に流し、意見が割れたものを優先的に人手レビューに回す、という段階的な運用と組み合わせる&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;gold_verified&lt;/span&gt; &lt;span class="err"&gt;読みが一意で人手検証済み&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;gold_free_variation&lt;/span&gt; &lt;span class="err"&gt;本質的に複数の読みがありうる&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;silver_ambiguous&lt;/span&gt; &lt;span class="err"&gt;まだ判断がついていない&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="自動ラベリングツールのバイアスの向きが語や構文によって不規則"&gt;自動ラベリングツールのバイアスの向きが語や構文によって不規則&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;ツール全体に対する一般的な補正ルールを当てはめるのではなく、語ごとに確認する必要がある&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;語A: 文語的な読みに寄る
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;語B: 特定の言い回しへの過学習で逆方向に寄る
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="ルビと原文の対応のズレ"&gt;ルビと原文の対応のズレ&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;ルビの範囲が実際の熟語の境界とズレて記録されることがある&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;原文: 山田太郎
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ルビ範囲（誤）: 山田太[ろう]
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ルビ範囲（正）: 山田[たろう]
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="分布統計レベル"&gt;分布・統計レベル&lt;/h3&gt;
&lt;h4 id="クラス不均衡ロングテール分布による少数派読みの学習バイアス"&gt;クラス不均衡・ロングテール分布による少数派読みの学習バイアス&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;出現頻度の差が大きいと、少数派の読みが十分に学習されなくなる&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;方 → ホウ（多数派）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;方 → カタ（少数派、学習不足）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="候補生成漏れとランカーの多数派バイアスの混同"&gt;「候補生成漏れ」と「ランカーの多数派バイアス」の混同&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;前者は辞書へのエントリ追加で解決できるが、後者は正解が候補集合に含まれているにもかかわらず、学習不足な文脈で統計的に優勢な読みへ倒れてしまう問題で、辞書追加だけでは解決しない&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;候補生成漏れ: candidates = [ホウ] → 辞書にカタを追加すれば直る
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ランカーのバイアス: candidates = [ホウ, カタ] → 追加しても依然ホウを選んでしまう
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="候補の並び順が暗黙の既定値として使われてしまう問題"&gt;候補の並び順が暗黙の既定値として使われてしまう問題&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;呼び出し側が判断に迷った際に先頭の候補へフォールバックする実装は多いが、候補の並び順が実際の多数派の読みと系統的に食い違っていることがある&lt;/li&gt;
&lt;li&gt;フォールバック処理そのものを直すより、候補テーブルの並び順自体を実際の頻度に合わせて直す方が、影響範囲が広く効果的&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;candidates[0] = ホウ　（テーブル上の並び順）
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;実際の多数派 = カタ
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="診断データセットの設計重複データの扱い"&gt;診断データセットの設計・重複データの扱い&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;ランダムサンプリングだけでは、少数派の意味的カテゴリを十分にカバーできないため、意図的にカテゴリを設計した診断データセットが必要になる&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;diagnostic_set:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 熟字訓カテゴリ: 意図的に一定数を含める
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 固有名詞カテゴリ: 意図的に一定数を含める
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="実装インフラレベル"&gt;実装・インフラレベル&lt;/h3&gt;
&lt;h4 id="実装ドリフト"&gt;実装ドリフト&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;同じ正規化ロジックが複数箇所に個別実装され、一方だけ修正されてもう一方が取り残され、時間とともに挙動が乖離していく&lt;/li&gt;
&lt;li&gt;いわば「データのバグ」ではなく「バグを生み出す仕組み側のバグ」&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;normalize_choon() # モジュールAで修正済み
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;normalize_choon() # モジュールBは未修正のまま残存
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="派生データの陳腐化"&gt;派生データの陳腐化&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;「コードを直した」ことと「データが直った」ことは別で、複数の修正が反映されないまま蓄積すると、影響範囲の把握そのものが難しくなる&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;生成元コード修正 → train.jsonl は自動更新されない → 再生成するまで古いラベルのまま
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="評価インフラ自体のバグ母集団ドリフト"&gt;評価インフラ自体のバグ・母集団ドリフト&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;訓練データだけでなく、物差しである評価パイプライン自体が壊れていないかも確認が必要&lt;/li&gt;
&lt;li&gt;意外に大きい数字を見たときほど、まず測定方法自体を疑うべき&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;「候補に正解が見当たらない」という見た目の大きな問題
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → 実際の原因のほとんどは、評価セットとモデル出力の間の長音符表記のズレ
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; → 実質的な問題はごく一部にすぎなかった
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="カンニング防止の射程"&gt;カンニング防止の射程&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;評価データそのものを学習に混入させない、という原則は当然守るべきだが、その射程は評価ファイル本体だけにとどまらない&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;eval_set&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;jsonl&lt;/span&gt; &lt;span class="err"&gt;直接の混入対象&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;tool_docs&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;schema&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;md&lt;/span&gt; &lt;span class="err"&gt;具体例が埋め込まれた、見落としやすい混入経路&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="データを直す方法論"&gt;データを直す方法論&lt;/h2&gt;
&lt;p&gt;個別のバグの分類以上に汎用性が高いと感じたのは、バグをどう見つけてどう直すかという方法論そのもの。&lt;/p&gt;
&lt;h4 id="盲検サンプリングとwilson信頼区間"&gt;盲検サンプリングとWilson信頼区間&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;少数のサンプルを見て「綺麗そう」と判断するのは危険で、区間推定をせずに小さいnの結果を信用しないようにする&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;n=100, 誤り=0件 → 「誤り率0%」ではなく、信頼区間の上限を踏まえて判断する
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="ソース別の層化監査"&gt;ソース別の層化監査&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;複数のソースを混ぜてランダムサンプリングすると、量の多いソースに埋もれて、量の少ないソースの汚染を見落とす&lt;/li&gt;
&lt;li&gt;ソースごとに独立したサンプルと信頼区間を持つことで、健全に見える全体平均の中に汚染された一つのソースが隠れているケースを検出できる&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ソースA（大量）: n=500, エラー率 0.5%
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ソースB（少量）: n=20, エラー率 15% ← 混ぜた平均では見えない
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="層化サンプリングとランダムサンプリングの往復"&gt;層化サンプリングとランダムサンプリングの往復&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;既知のリスクがある語を狙い撃ちする層化サンプリングは、既知パターンの再発を効率よく見つけられるが、想定していなかった問題は見つけられない&lt;/li&gt;
&lt;li&gt;純粋なランダムサンプリングと交互に行い、両方の死角を補い合う&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;層化サンプリング: 過去に問題のあった語を優先的に再チェック
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;ランダムサンプリング: 誰も疑っていない箇所を無作為にチェック
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="修正のたびに監査をやり直す"&gt;修正のたびに監査をやり直す&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;一度きりの監査ではなく、何かを見つけて直すたびに、ゼロからサンプルを取り直して確認する&lt;/li&gt;
&lt;li&gt;修正が実際に効いたか、そして新たな問題を持ち込んでいないかを、この再監査で確かめる&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="検証済みも定期的に再監査する"&gt;「検証済み」も定期的に再監査する&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;一度「検証済み」に分類したデータも、時間をおいて再度サンプル監査する&lt;/li&gt;
&lt;li&gt;「検証済み」は保証ではなく、あくまである時点での確信度に過ぎない&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="何もなくても定期的に健全性を確認する"&gt;何もなくても定期的に健全性を確認する&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;既知の問題が特に見つかっていない期間でも、一定のペースでサンプル監査を続ける&lt;/li&gt;
&lt;li&gt;何かのトリガーに反応するだけでなく、確信度を維持するための定期点検として運用する&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="網羅的なシグネチャスキャンとランダムサンプリングの併用"&gt;網羅的なシグネチャスキャンとランダムサンプリングの併用&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;既知のパターンに一致するものを網羅的に検出する方法と、未知のパターンを見つけるためのランダムサンプリングは、どちらか一方では不十分で、両方を使い分ける&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;既知パターン（濁点脱落など） → 全件シグネチャスキャン
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;未知パターン → ランダムサンプリング
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="独立したクロスチェックソースの利用"&gt;独立したクロスチェックソースの利用&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;単一の辞書・コーパスだけを信頼せず、複数の独立したソースを突き合わせる&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;辞書A + 辞書B + コーパス頻度 → 一致しないものだけを優先的にレビュー
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="自分自身のノイズフロアを測る"&gt;自分自身のノイズフロアを測る&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;何かを変更してスコアが動いたとき、それが本当に変更の効果なのか、単なる実行間のばらつき（ノイズ）なのかを、同一設定での再実行によって確認する&lt;/li&gt;
&lt;li&gt;この確認を怠ると、ノイズを実質的な改善・劣化と誤認し、的外れな判断を積み重ねてしまう&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;変更あり: score = 0.893
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;変更なし（再実行1）: score = 0.891
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;変更なし（再実行2）: score = 0.895
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="内部矛盾スキャン"&gt;内部矛盾スキャン&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;同じ表記に対して複数の異なる読みが記録されているケースだけを機械的に抽出し、その組だけをレビューする&lt;/li&gt;
&lt;li&gt;全体をランダムサンプリングするより、ずっと高い効率で誤りを見つけられる&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;surface=&amp;#34;方&amp;#34; → readings={ホウ, カタ} # レビュー対象として抽出
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="一括訂正の副作用に注意する"&gt;一括訂正の副作用に注意する&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;ある語の読みが間違っていたと分かったとき、その語の全出現箇所を一括で訂正すると効率は良いが、別の箇所では実は正しかった、という文脈依存のケースまで巻き込んで壊してしまうことがある&lt;/li&gt;
&lt;li&gt;訂正自体が新しい誤りの発生源になりうる&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;「方」の誤ラベルを一括でカタ→ホウに訂正
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ 本来カタが正しかった別の出現箇所まで巻き込んで書き換えてしまう
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="検証ツール自体を疑う"&gt;検証ツール自体を疑う&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;検証ツールは「正しい前提」として扱いがちだが、それ自体もコードである以上、同じ種類のバグを持ちうる&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;cross_check_script.py に normalize_choon() と同じバグが混入
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;→ 大量の偽陽性を生成
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="表記のズレを見つけたら生データソースまで遡る"&gt;表記のズレを見つけたら生データソースまで遡る&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;訓練データの中で表記が壊れているのを見つけたとき、それが下流の処理で壊れたのか、元の生データの時点でそう記録されていたのかを、まず切り分ける&lt;/li&gt;
&lt;li&gt;話し言葉の書き起こしとして元々そう記録されていた場合、それは「バグ」ではなく、正しい実態を反映した表記ゆれということもある&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;raw_source → 正常
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓ 前処理
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;train.jsonl → 破損　→ 前処理側のバグと判明
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="コードが物語る大変さ"&gt;コードが物語る大変さ&lt;/h2&gt;
&lt;p&gt;ここまではbacklog・docs・タスク記録という「言語化された記録」をもとに整理してきたが、実際のコードとテストを読むと、記録だけでは伝わらない大変さが見えてくる。&lt;/p&gt;
&lt;h4 id="一つ一つが個別に勝ち取られた事実の集合"&gt;一つ一つが個別に勝ち取られた事実の集合&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;補正関数やルックアップテーブルの各エントリは、まとめて取り込んだものではなく、それぞれ個別の調査によって発見・検証された事実&lt;/li&gt;
&lt;li&gt;過去の修正が広すぎた、あるいは狭すぎたと後から判明し、範囲を絞り直された跡も残っている&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;counter_word_sound_change_table:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; # エントリ1件ごとに、それを発見した具体的な調査の痕跡が残っている
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="正例と負例をペアにしたテスト"&gt;正例と負例をペアにしたテスト&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;ほぼすべての修正に対して、狭いケースが正しく処理されることを確認するテストと、似ているが別のケースには意図的に手を付けていないことを確認するテストが、ペアで存在する&lt;/li&gt;
&lt;li&gt;難しさの本質は「誤りを見つけること」ではなく「他を壊さないように修正の範囲を絞り込むこと」にあり、そのスコープの判断が回帰テストとして恒久的に固定化されている&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;test_counter_word_change_applies_to_specific_pattern()
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;test_counter_word_change_does_not_apply_to_similar_pattern()
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="先送りにされた妥協がない"&gt;先送りにされた妥協がない&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;TODO・HACK・FIXMEのようなショートカットの印が実質存在しない&lt;/li&gt;
&lt;li&gt;見つかった問題は先送りにされず、スコープを絞った修正として実装まで持ち込まれている&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="モジュールのdocstringが実験ノートとして機能している"&gt;モジュールのdocstringが実験ノートとして機能している&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;ドキュメントというより、試して失敗したアプローチの記録として機能している&lt;/li&gt;
&lt;li&gt;複数の補正戦略が試され、悪化したために元に戻された、という記述も残っている&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;記録は「何を発見したか」を語るが、コード自体は「それぞれの発見を、他を壊さずに正しく直すことの方が、発見そのものよりはるかに労力のかかる半分の作業だった」ことを物語っている。&lt;/p&gt;
&lt;h2 id="まとめ"&gt;まとめ&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;日本語のフリガナデータで実際に遭遇した問題を整理すると、文字・表記、読み表記、意味・文脈、分割・アライメント、ソース・アノテーション品質、分布・統計、実装・インフラという複数のレベルにまたがっていた&lt;/li&gt;
&lt;li&gt;特に、形態素解析の分割・融合ミスや、実装ドリフト、評価インフラ自体のバグは、訓練データそのものの中身の問題とは異なる、仕組み側の問題として区別して扱う必要がある&lt;/li&gt;
&lt;li&gt;個別のバグより汎用性が高いのは、盲検サンプリング・ソース別の層化監査・Wilson信頼区間・独立クロスチェック・ノイズフロアの計測といった、データを直す際の方法論そのものだった&lt;/li&gt;
&lt;li&gt;コードとテストを読むと、誤りを見つけることより、他を壊さずに正しく直すことの方がはるかに労力のかかる作業だったことが分かる&lt;/li&gt;
&lt;li&gt;「データが完璧なら性能が出る」という単純な話ではなく、どのレベルで何が壊れているかを見極める継続的な作業そのものが、データを正しくするということの実態だと感じている&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="参考文献"&gt;参考文献&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval" target="_blank" rel="noopener"
&gt;Binomial proportion confidence interval - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>安定化モードとseedの検定方法</title><link>https://www.m1ke.org/p/%E5%AE%89%E5%AE%9A%E5%8C%96%E3%83%A2%E3%83%BC%E3%83%89%E3%81%A8seed%E3%81%AE%E6%A4%9C%E5%AE%9A%E6%96%B9%E6%B3%95/</link><pubDate>Sat, 05 Sep 2026 13:08:25 +0900</pubDate><guid>https://www.m1ke.org/p/%E5%AE%89%E5%AE%9A%E5%8C%96%E3%83%A2%E3%83%BC%E3%83%89%E3%81%A8seed%E3%81%AE%E6%A4%9C%E5%AE%9A%E6%96%B9%E6%B3%95/</guid><description>&lt;img src="https://www.m1ke.org/p/%E5%AE%89%E5%AE%9A%E5%8C%96%E3%83%A2%E3%83%BC%E3%83%89%E3%81%A8seed%E3%81%AE%E6%A4%9C%E5%AE%9A%E6%96%B9%E6%B3%95/t_test.png" alt="Featured image of post 安定化モードとseedの検定方法" /&gt;&lt;h2 id="背景"&gt;背景&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;毎回このコードを最初に貼り付けるが、いつも見失う&lt;/li&gt;
&lt;li&gt;なのでgistとしてここに残す&lt;/li&gt;
&lt;li&gt;機械学習で毎回seedを固定するためのもの&lt;/li&gt;
&lt;li&gt;非決定的にせずに実験をスムーズにするための設定&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="課題"&gt;課題&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;機械学習での実験の課題はモデルの評価&lt;/li&gt;
&lt;li&gt;新しいネットワーク構造にしても差が数ポイントだったりする&lt;/li&gt;
&lt;li&gt;そうすると、ノイズフロアによる誤差なのかがわからない&lt;/li&gt;
&lt;li&gt;そこで、学習や推論を&lt;strong&gt;できるだけ&lt;/strong&gt;決定的（deterministic）にして再現性を高める&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="非決定的"&gt;非決定的&lt;/h2&gt;
&lt;h3 id="非決定的とランダムの違い"&gt;非決定的とランダムの違い&lt;/h3&gt;
&lt;p&gt;非決定的と似た概念としてランダムがあるが、同じではない。&lt;/p&gt;
$$
非決定的 \neq ランダム
$$&lt;ul&gt;
&lt;li&gt;ランダム化アルゴリズム
&lt;ul&gt;
&lt;li&gt;実際に乱数を使って1つの経路を選ぶ&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;非決定的アルゴリズム
&lt;ul&gt;
&lt;li&gt;理論上、複数の選択肢から「正解につながる選択ができる」と考える計算モデル&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;NOTE: ただしPyTorchのバージョン・プラットフォーム・CPU/GPUをまたいだ完全な再現性までは保証されない。&lt;/p&gt;
&lt;h3 id="非決定的という言葉について"&gt;非決定的という言葉について&lt;/h3&gt;
&lt;p&gt;計算量理論でいう非決定的アルゴリズムと、PyTorchでいうnondeterministic operationは意味が異なる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;計算量理論の non-deterministic
&lt;ul&gt;
&lt;li&gt;複数の計算経路の中から正解につながる経路を選べると仮定する理論上の計算モデル&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;PyTorchやGPU計算のnon-deterministic
&lt;ul&gt;
&lt;li&gt;並列計算や演算順序などにより、同じ入力でも実行ごとに数値結果がわずかに変わることがある&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;seed固定と deterministic algorithmの利用は、後者を抑えて実験の再現性を高めるために行う。&lt;/p&gt;
&lt;h2 id="安定化モード"&gt;安定化モード&lt;/h2&gt;
&lt;h3 id="安定化モードのコード"&gt;安定化モードのコード&lt;/h3&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manual_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cuda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manual_seed_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;enable_determinism&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cudnn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deterministic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cudnn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;benchmark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cuda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;matmul&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;allow_tf32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cudnn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;allow_tf32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;use_deterministic_algorithms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;warn_only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;use_deterministic_algorithms(True, warn_only=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;strict&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;) &amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;+ TF32 無効化 + cudnn.deterministic=True&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 実行&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;seed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;set_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;enable_determinism&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;set_seed / enable_determinism 定義完了&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="安定化モードの注意点"&gt;安定化モードの注意点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;たとえ、あるseedに固定して決定的にして実験して精度が出せても、それが完全に優れているとは限らない&lt;/li&gt;
&lt;li&gt;なぜなら、ある条件A（仮にseed=42）の時の複数の実験結果の精度比較に過ぎないから&lt;/li&gt;
&lt;li&gt;つまり、複数seedでの平均・標準偏差などを見て最終的には評価する必要がある&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;たとえば、以下の結果だったとする。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;モデルA seed=42 → 90.2%
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;モデルB seed=42 → 90.8%
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;しかし、seedを変えたら、以下になったら、Bが良いとは言い難い。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;seed A B
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;1 90.5 90.0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;2 89.8 90.3
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;3 90.4 90.1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;4 89.9 90.5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;5 90.2 90.0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="統計的に評価する"&gt;統計的に評価する&lt;/h2&gt;
&lt;p&gt;複数seedで実験した結果、その差が統計的に意味のあるものかを確認する方法がいくつか提案されている。&lt;/p&gt;
&lt;h3 id="検定の分類"&gt;検定の分類&lt;/h3&gt;
&lt;p&gt;良い図があったので、これを載せる。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.m1ke.org/p/%E5%AE%89%E5%AE%9A%E5%8C%96%E3%83%A2%E3%83%BC%E3%83%89%E3%81%A8seed%E3%81%AE%E6%A4%9C%E5%AE%9A%E6%96%B9%E6%B3%95/kentei.png"
width="2356"
height="2066"
srcset="https://www.m1ke.org/p/%E5%AE%89%E5%AE%9A%E5%8C%96%E3%83%A2%E3%83%BC%E3%83%89%E3%81%A8seed%E3%81%AE%E6%A4%9C%E5%AE%9A%E6%96%B9%E6%B3%95/kentei_hu_63d240d51262145.png 480w, https://www.m1ke.org/p/%E5%AE%89%E5%AE%9A%E5%8C%96%E3%83%A2%E3%83%BC%E3%83%89%E3%81%A8seed%E3%81%AE%E6%A4%9C%E5%AE%9A%E6%96%B9%E6%B3%95/kentei_hu_abd402bc36c7fcfe.png 1024w"
loading="lazy"
alt="検定の全体図"
class="gallery-image"
data-flex-grow="114"
data-flex-basis="273px"
&gt;&lt;/p&gt;
&lt;h3 id="t検定平均とsdから直接比較する"&gt;t検定（平均とSDから直接比較する）&lt;/h3&gt;
&lt;p&gt;最もシンプルな方法は、平均と標準偏差からそのままt検定を行うこと。&lt;/p&gt;
$$
t = \frac{\bar{x}_A - \bar{x}_B}{\sqrt{\dfrac{s_A^2}{n_A} + \dfrac{s_B^2}{n_B}}}
$$&lt;ul&gt;
&lt;li&gt;$\bar{x}_A, \bar{x}_B$: それぞれのモデルの複数seedでの平均&lt;/li&gt;
&lt;li&gt;$s_A, s_B$: それぞれのSD&lt;/li&gt;
&lt;li&gt;$n_A, n_B$: seed数&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;$t$の絶対値が大きいほど、平均の差がSD（ばらつき）に対して十分大きい、つまり偶然とは考えにくいと判断できる。&lt;/p&gt;
&lt;h3 id="分布を仮定しない方法"&gt;分布を仮定しない方法&lt;/h3&gt;
&lt;p&gt;t検定は、データが正規分布に近いという前提を置いている。seed数が5個などと少ない場合、この前提が怪しくなる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Kolmogorov-Smirnov検定やブートストラップ法は、分布の形を仮定しない、より頑健な代替手段になる&lt;/li&gt;
&lt;li&gt;Henderson et al.は、強化学習分野でseedによる結果のばらつきが無視できないほど大きいことを指摘し、2標本t検定・Kolmogorov-Smirnov検定・ブートストラップ法による有意性検定を推奨している&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="必要なseed数を見積もる"&gt;必要なseed数を見積もる&lt;/h3&gt;
&lt;p&gt;「優劣を出す」話とは別に、「そもそも何seed回せば信頼できる結論が出せるか」という、実験を始める前の設計の問題もある。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;検定力分析（power analysis）で、必要なseed数を統計的に見積もることができる&lt;/li&gt;
&lt;li&gt;Colas et al.は強化学習実験を例に、この考え方を具体的に示している&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="探索コストの公平性"&gt;探索コストの公平性&lt;/h3&gt;
&lt;p&gt;比較する2つのモデルが、同じ探索コストをかけて選ばれたかという、また別の前提条件もある。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dodge et al.は、単一のtest scoreだけでなく、ハイパーパラメータ探索にかけた計算量（試行回数）に応じた期待検証性能を報告することを提案している&lt;/li&gt;
&lt;li&gt;探索に使った計算量が違うモデル同士を比較すると、優劣の判定自体が公平でなくなる&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="実用ツール"&gt;実用ツール&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;深層学習向けにこうした検定を簡単に行うための&lt;code&gt;deep-significance&lt;/code&gt;というPythonライブラリも公開されている&lt;/li&gt;
&lt;li&gt;有意検定（Significance Testing）によって、調査結果の有意性に関してある仮説をたてて、それが成り立つか否かを現実のデータでもって判断することが可能&lt;/li&gt;
&lt;li&gt;単純に複数seedの平均を比べるだけでなく、その差が統計的に有意かどうかまで確認するのが望ましい。&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="その他"&gt;その他&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ちなみに、seedで42がよく使われるのは、銀河ヒッチハイクガイドからのオマージュ&lt;/li&gt;
&lt;li&gt;その小説の中の「生命、宇宙、そして万物についての究極の疑問の答え」= 42という答えから引用している
&lt;ul&gt;
&lt;li&gt;see &lt;a class="link" href="https://ja.wikipedia.org/wiki/%E7%94%9F%E5%91%BD%E3%80%81%E5%AE%87%E5%AE%99%E3%80%81%E3%81%9D%E3%81%97%E3%81%A6%E4%B8%87%E7%89%A9%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E3%81%AE%E7%A9%B6%E6%A5%B5%E3%81%AE%E7%96%91%E5%95%8F%E3%81%AE%E7%AD%94%E3%81%88" target="_blank" rel="noopener"
&gt;生命、宇宙、そして万物についての究極の疑問の答え - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;個人的には、最高の答えが42だとしたら、最高の質問は存在の質問「Why is there something rather than nothing?」だと思う
&lt;ul&gt;
&lt;li&gt;see &lt;a class="link" href="https://ja.wikipedia.org/wiki/%E3%81%AA%E3%81%9C%E4%BD%95%E3%82%82%E3%81%AA%E3%81%84%E3%81%AE%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%8F%E3%80%81%E4%BD%95%E3%81%8B%E3%81%8C%E3%81%82%E3%82%8B%E3%81%AE%E3%81%8B" target="_blank" rel="noopener"
&gt;なぜ何もないのではなく、何かがあるのか - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;それらを組み合わせると、なぜなぜ究極の答えは、42だったと&lt;/li&gt;
&lt;li&gt;ただ、個人的には、モンスター群の196883の方が真理に近い気がして好きな数字
&lt;ul&gt;
&lt;li&gt;see &lt;a class="link" href="https://ja.wikipedia.org/wiki/%E3%83%A2%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%BC%E7%BE%A4" target="_blank" rel="noopener"
&gt;モンスター群 - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="まとめ"&gt;まとめ&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;モデル変更による数pointの差を比較するときは、seedや演算を固定して実験を決定的にすることで、ばらつきを抑えられる&lt;/li&gt;
&lt;li&gt;これは開発中のデバッグやablationでは非常に便利で、変更前後を同条件で比較しやすくなる&lt;/li&gt;
&lt;li&gt;一方、最終的にモデル性能の改善を主張する場合は、単一seedでの結果だけでは不十分&lt;/li&gt;
&lt;li&gt;つまり、複数seedで実験して平均や分散を見る必要がある&lt;/li&gt;
&lt;li&gt;determinismは統計評価の代替ではなく、実験条件を揃えるための手段ということ&lt;/li&gt;
&lt;li&gt;統計的保証に関しては、個人的にはt検定をすれば十分だと思っている&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="参考文献"&gt;参考文献&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.geeksforgeeks.org/dsa/difference-between-deterministic-and-non-deterministic-algorithms/" target="_blank" rel="noopener"
&gt;Difference between Deterministic and Non-deterministic Algorithms - GeeksforGeeks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.pytorch.org/docs/2.14/notes/randomness.html?utm_source=chatgpt.com" target="_blank" rel="noopener"
&gt;Reproducibility — PyTorch 2.14 documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://codecrucks.com/deterministic-and-non-deterministic-algorithms/" target="_blank" rel="noopener"
&gt;Deterministic and Non-Deterministic Algorithms - CodeCrucks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://gigazine.net/news/20210203-physical-constants-42/" target="_blank" rel="noopener"
&gt;生命、宇宙、すべての究極の質問への答え「42」は真実なのか？ - GIGAZINE&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://arxiv.org/abs/1709.06560" target="_blank" rel="noopener"
&gt;Deep Reinforcement Learning that Matters&lt;/a&gt;（Henderson et al., AAAI 2018）&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://arxiv.org/abs/1806.08295" target="_blank" rel="noopener"
&gt;How Many Random Seeds? Statistical Power Analysis in Deep Reinforcement Learning Experiments&lt;/a&gt;（Colas et al.）&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://arxiv.org/abs/1909.03004" target="_blank" rel="noopener"
&gt;Show Your Work: Improved Reporting of Experimental Results&lt;/a&gt;（Dodge et al., EMNLP 2019）&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://arxiv.org/abs/2204.06815" target="_blank" rel="noopener"
&gt;deep-significance: Easy and Meaningful Statistical Significance Testing in the Age of Neural Networks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://sb8001at-oss.github.io/Rnyuumon.io/chapter29.html" target="_blank" rel="noopener"
&gt;29  検定 – R入門&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.geeksforgeeks.org/data-science/t-test/" target="_blank" rel="noopener"
&gt;T-test - GeeksforGeeks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://ja.wikipedia.org/wiki/%E7%94%9F%E5%91%BD%E3%80%81%E5%AE%87%E5%AE%99%E3%80%81%E3%81%9D%E3%81%97%E3%81%A6%E4%B8%87%E7%89%A9%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E3%81%AE%E7%A9%B6%E6%A5%B5%E3%81%AE%E7%96%91%E5%95%8F%E3%81%AE%E7%AD%94%E3%81%88" target="_blank" rel="noopener"
&gt;生命、宇宙、そして万物についての究極の疑問の答え - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://ja.wikipedia.org/wiki/%E3%81%AA%E3%81%9C%E4%BD%95%E3%82%82%E3%81%AA%E3%81%84%E3%81%AE%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%8F%E3%80%81%E4%BD%95%E3%81%8B%E3%81%8C%E3%81%82%E3%82%8B%E3%81%AE%E3%81%8B" target="_blank" rel="noopener"
&gt;なぜ何もないのではなく、何かがあるのか - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://ja.wikipedia.org/wiki/%E3%83%A2%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%BC%E7%BE%A4" target="_blank" rel="noopener"
&gt;モンスター群 - Wikipedia&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>機械学習における評価指標の再考</title><link>https://www.m1ke.org/p/%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E8%A9%95%E4%BE%A1%E6%8C%87%E6%A8%99%E3%81%AE%E5%86%8D%E8%80%83/</link><pubDate>Sat, 05 Sep 2026 12:00:00 +0900</pubDate><guid>https://www.m1ke.org/p/%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E8%A9%95%E4%BE%A1%E6%8C%87%E6%A8%99%E3%81%AE%E5%86%8D%E8%80%83/</guid><description>&lt;img src="https://www.m1ke.org/p/%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E8%A9%95%E4%BE%A1%E6%8C%87%E6%A8%99%E3%81%AE%E5%86%8D%E8%80%83/split.jpg" alt="Featured image of post 機械学習における評価指標の再考" /&gt;&lt;h2 id="背景"&gt;背景&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;機械学習では、データをTrain・Validation・Testの3つに分けるのが定石とされている&lt;/li&gt;
&lt;li&gt;Trainでモデルを学習し、ValidationでハイパーパラメータやEarly Stoppingを決め、最後にTestで未知データに対する性能を測るという構造&lt;/li&gt;
&lt;li&gt;しかし実際にシステムを開発していると、この3分割だけでは説明できない問題にぶつかることがある&lt;/li&gt;
&lt;li&gt;特に「Testで失敗している理由を知りたいが、Testを見て改善した瞬間、そのTestはもはやTestではなくなる」という問題がある&lt;/li&gt;
&lt;li&gt;ここでは、Train・Val・Testという古典的な3分割を、DiagとChallengeを加えた5つの役割に拡張して整理する&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="trainvaltestの役割"&gt;Train・Val・Testの役割&lt;/h2&gt;
&lt;p&gt;まず、通常の3分割それぞれの役割を整理する。&lt;/p&gt;
&lt;h3 id="train"&gt;Train&lt;/h3&gt;
&lt;p&gt;モデルのパラメータを学習するためのデータ。以下の式で表される、学習データ上での損失を最小化するパラメータ$\theta^*$を求めるために使う。&lt;/p&gt;
$$
\theta^* = \arg\min_\theta \hat{R}_{\text{train}}(\theta)
$$&lt;h3 id="validation"&gt;Validation&lt;/h3&gt;
&lt;p&gt;モデルそのものではなく、学習方法を選択するためのデータ。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Learning Rate&lt;/li&gt;
&lt;li&gt;モデルサイズ&lt;/li&gt;
&lt;li&gt;正則化係数&lt;/li&gt;
&lt;li&gt;Data Augmentation&lt;/li&gt;
&lt;li&gt;Early Stopping&lt;/li&gt;
&lt;li&gt;モデルアーキテクチャ&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これらを選ぶために使う。つまりValidationも、広い意味では学習プロセスの一部といえる。&lt;/p&gt;
&lt;h3 id="test"&gt;Test&lt;/h3&gt;
&lt;p&gt;最後までモデル開発から隔離し、最終的な汎化性能を測定するためのデータ。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Testを見て意思決定してはいけないという原則が重要&lt;/li&gt;
&lt;li&gt;Testを見ながらモデルを改善すると、モデルの重みを直接学習していなくても、開発者自身がTestに適応してしまう&lt;/li&gt;
&lt;li&gt;結果として、Testへの「開発プロセス全体の過学習」が起きる&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="train-val-testの違い"&gt;train, val, testの違い&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;train data：重み更新に使う&lt;/li&gt;
&lt;li&gt;validation data：学習中のモデル選択、early stopping、ハイパーパラメータ調整に使う&lt;/li&gt;
&lt;li&gt;test data：最後の最終評価に使う&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="train分布と本番分布が違うという問題"&gt;Train分布と本番分布が違うという問題&lt;/h2&gt;
&lt;p&gt;古典的なTrain・Val・Testの説明には、以下のような暗黙の仮定がある。&lt;/p&gt;
$$
P_{\text{train}} \approx P_{\text{val}} \approx P_{\text{test}} \approx P_{\text{deployment}}
$$&lt;p&gt;しかし、実運用ではこの仮定は頻繁に崩れる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TrainはWeb画像だが、本番はスマートフォン画像&lt;/li&gt;
&lt;li&gt;Trainは過去のユーザーだが、本番は将来のユーザー&lt;/li&gt;
&lt;li&gt;Trainはある病院のデータだが、本番は別の病院&lt;/li&gt;
&lt;li&gt;Trainはある国のデータだが、本番は別地域&lt;/li&gt;
&lt;li&gt;Train時とDeployment時で商品の構成が変わる&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;WILDSベンチマークは、病院・撮影場所・時間・地域など、実世界で自然に発生するDistribution Shiftによって、In-distribution性能とOut-of-distribution性能の間に大きな差が生じることを示している。つまり、以下の不等式で表される状況は例外ではなく、実世界ではかなり普通に起きる。&lt;/p&gt;
$$
P_{\text{source}} \neq P_{\text{target}}
$$&lt;h2 id="testのパラドックス"&gt;Testのパラドックス&lt;/h2&gt;
&lt;p&gt;例えば、最終Testで性能が大きく低下したとする。Val Accuracyが94%なのに、Test Accuracyが72%だったとすると、開発者は当然「なぜ22ポイントも落ちたのか」を知りたくなる。そこでTestデータを見ると、以下のようなことが分かる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;暗い画像で失敗している&lt;/li&gt;
&lt;li&gt;特定の端末だけ性能が低い&lt;/li&gt;
&lt;li&gt;特定カテゴリのRecallが悪い&lt;/li&gt;
&lt;li&gt;Trainに存在しない背景が多い&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これがError Analysis。この分析結果を元に、暗所画像をTrainに追加する、Augmentationを変更する、ラベル設計を変更する、モデル構造を変更する、といった改善を行う。&lt;/p&gt;
&lt;p&gt;しかし、この瞬間に問題が起きる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Testから得られた情報でモデルを改善した以上、そのTestはもう完全なBlind Testではない&lt;/li&gt;
&lt;li&gt;形式的にモデルパラメータへTestを入力していなくても、Test→人間→モデル設計という情報経路が成立している&lt;/li&gt;
&lt;li&gt;つまりTestは、実質的に開発用データセットになってしまう&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="target側にもdiagを作る"&gt;Target側にもDiagを作る&lt;/h2&gt;
&lt;p&gt;ここで自然な解決策として、Source側だけでなくTarget側も分割するという発想が出てくる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Source側: Train、Val&lt;/li&gt;
&lt;li&gt;Target側: Diag、Test&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;という構造になる。&lt;/p&gt;
&lt;h2 id="diagとtestの違い"&gt;diagとtestの違い&lt;/h2&gt;
&lt;h3 id="diag"&gt;Diag&lt;/h3&gt;
&lt;p&gt;DiagはTarget distributionを理解するためのデータで、開発者が見てよい。サンプルを直接確認し、以下のような分析をする。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Failure Mode&lt;/li&gt;
&lt;li&gt;Slice別性能&lt;/li&gt;
&lt;li&gt;分布差&lt;/li&gt;
&lt;li&gt;ラベル問題&lt;/li&gt;
&lt;li&gt;Spurious Correlation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Diagから得られた知見は、Trainへ戻して改善に使う。&lt;/p&gt;
&lt;h3 id="test-1"&gt;Test&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Target distributionから採取するが、最後までBlindにする&lt;/li&gt;
&lt;li&gt;最終的に、「Diagを見ながら作った開発プロセスが、別のTargetサンプルにもGeneralizeしたか」を確認する&lt;/li&gt;
&lt;li&gt;つまり、Train→Val→Diag→Error Analysis→Trainという改善ループと、Model→Blind Testという最終評価を分離する構造になる&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="diagだけでも足りない理由"&gt;Diagだけでも足りない理由&lt;/h2&gt;
&lt;p&gt;もう一つ問題がある。平均的なDiag performanceだけでは、モデルが持っている重要な弱点を発見できないことがある。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;例えば自動運転モデルで、全体のAccuracyが99.5%だったとする&lt;/li&gt;
&lt;li&gt;夜間・豪雨・逆光・工事現場・子どもの飛び出しといった状況だけで極端に性能が低ければ、実運用では重大な問題になる&lt;/li&gt;
&lt;li&gt;しかし、これらが通常データの0.1%しか存在しなければ、平均Accuracyではほとんど見えない&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;そこで必要になるのがChallenge Set。&lt;/p&gt;
&lt;h2 id="challenge-setは分布を代表する必要がない"&gt;Challenge Setは分布を代表する必要がない&lt;/h2&gt;
&lt;p&gt;Challenge Setは、通常のTestとは目的が違う。Testは、以下のようにdeployment分布を代表することを目指す。&lt;/p&gt;
$$
P_{\text{test}} \approx P_{\text{deployment}}
$$&lt;p&gt;一方Challengeは、必ずしもDeployment distributionを代表しなくてよい。むしろ意図的に難しいケースを集める。目的が違うため、以下のように分布が一致していなくても構わない。&lt;/p&gt;
$$
P_{\text{challenge}} \neq P_{\text{deployment}}
$$&lt;p&gt;Challengeの目的は、平均性能を推定することではなく、特定の能力・弱点を診断すること。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ImageNet-Aのような研究はこの発想の典型例&lt;/li&gt;
&lt;li&gt;通常のImageNetとは異なり、既存モデルが失敗しやすい自然画像を意図的に集めることで、平均的なテストセットでは見えにくいモデルの弱点を明らかにする&lt;/li&gt;
&lt;li&gt;Challengeは1つである必要もなく、Failure Modeごとに複数持ってもよい
&lt;ul&gt;
&lt;li&gt;Challenge（低照度）&lt;/li&gt;
&lt;li&gt;Challenge（レアクラス）&lt;/li&gt;
&lt;li&gt;Challenge（OOD）&lt;/li&gt;
&lt;li&gt;Challenge（ロングテール）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="5つの役割"&gt;5つの役割&lt;/h2&gt;
&lt;p&gt;ここまで整理すると、評価データは以下のようになる。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dataset&lt;/th&gt;
&lt;th&gt;主目的&lt;/th&gt;
&lt;th style="text-align: right"&gt;開発者が見るか&lt;/th&gt;
&lt;th style="text-align: right"&gt;モデル改善に使用するか&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Train&lt;/td&gt;
&lt;td&gt;Parameter Learning&lt;/td&gt;
&lt;td style="text-align: right"&gt;Yes&lt;/td&gt;
&lt;td style="text-align: right"&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Val&lt;/td&gt;
&lt;td&gt;Model Selection&lt;/td&gt;
&lt;td style="text-align: right"&gt;Yes&lt;/td&gt;
&lt;td style="text-align: right"&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Diag&lt;/td&gt;
&lt;td&gt;Error Analysis&lt;/td&gt;
&lt;td style="text-align: right"&gt;Yes&lt;/td&gt;
&lt;td style="text-align: right"&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Challenge&lt;/td&gt;
&lt;td&gt;Failure-mode / Capability Evaluation&lt;/td&gt;
&lt;td style="text-align: right"&gt;Yes&lt;/td&gt;
&lt;td style="text-align: right"&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Test&lt;/td&gt;
&lt;td&gt;Final Generalization Estimate&lt;/td&gt;
&lt;td style="text-align: right"&gt;No&lt;/td&gt;
&lt;td style="text-align: right"&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;つまり、Train・Val・Diag・Challenge・Blind Testという構造&lt;/li&gt;
&lt;li&gt;ただし、これは「データを機械的に5分割せよ」という意味ではなく、重要なのはそれぞれの役割を分離すること&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="valとdiagの違い"&gt;ValとDiagの違い&lt;/h2&gt;
&lt;p&gt;一見するとValとDiagは同じものに見え、実際に多くの文献ではValidation Setと開発用データセットがほぼ同義語として使われる。ここではあえて分離して考える。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Val: どのモデルを選ぶかを決めるためのデータ&lt;/li&gt;
&lt;li&gt;Diag: なぜ本番で失敗するのかを理解するためのデータ&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;例:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;例えば、Val AccuracyでModel Aが91.2%、Model Bが92.1%ならModel Bを選ぶ&lt;/li&gt;
&lt;li&gt;これがModel Selection&lt;/li&gt;
&lt;li&gt;一方Diagでは、「Model Bは平均では良いが、夜間画像ではModel Aより悪い」といった分析をする&lt;/li&gt;
&lt;li&gt;これがError Analysis&lt;/li&gt;
&lt;li&gt;つまり、ValとDiagは役割が異なる&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="challengeとdiagの違い"&gt;ChallengeとDiagの違い&lt;/h2&gt;
&lt;p&gt;Diagは、Target distributionをなるべく代表する。一方Challengeは、意図的に分布を歪める。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;例えば本番データで、通常画像98%・夜間1%・豪雨0.5%・逆光0.5%だったとする&lt;/li&gt;
&lt;li&gt;Diagではこの比率をある程度維持する&lt;/li&gt;
&lt;li&gt;しかしChallengeでは、夜間33%・豪雨33%・逆光34%のように歪めてもよい&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;なぜならChallengeはPopulation Performanceではなく、Capabilityを測っているため。&lt;/p&gt;
&lt;h2 id="既存研究との関係"&gt;既存研究との関係&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ここで整理した5つの役割そのものが、機械学習の標準規格として確立されているわけではなかった&lt;/li&gt;
&lt;li&gt;むしろ、既存研究で別々に議論されてきた問題を、1つの開発フローとして整理したものと考える方が正確&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;先行研究のTopics:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;分布シフト（WILDS）
&lt;ul&gt;
&lt;li&gt;Koh et al.のWILDSは、現実世界ではTraining distributionとDeploymentに近いTest distributionが異なることを明示的に扱っている&lt;/li&gt;
&lt;li&gt;病院、地域、時刻、カメラなどによる実世界のDistribution Shiftをベンチマーク化しており、Source/Targetを分けて考える必要性を示している&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Target performanceの推定（Mandoline）
&lt;ul&gt;
&lt;li&gt;Chen et al.のMandolineは、SourceとTargetの分布差を、実務者が定義したSliceを利用してTarget上の性能推定に利用する&lt;/li&gt;
&lt;li&gt;「Target distributionを単一のAccuracyだけではなく、その構造を見ながら評価する」という、Diag的な発想に近い&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;分布シフトに対するStress Test
&lt;ul&gt;
&lt;li&gt;Subbaswamy et al.は、単一の評価分布で平均性能を見るだけでなく、分布を変化させた際のモデルのRobustness・Stabilityを評価する枠組みを提案している&lt;/li&gt;
&lt;li&gt;Challenge SetやStress Testingの考え方と接続する&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Challenge Set（ImageNet-A）
&lt;ul&gt;
&lt;li&gt;Hendrycks et al.のImageNet-Aは、既存モデルが失敗しやすい自然画像を意図的に集めることで、通常のTest Accuracyでは見えにくい弱点を評価した、Challenge evaluationの分かりやすい例&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Testから学習してしまう問題
&lt;ul&gt;
&lt;li&gt;Hernández-Orallo et al.は、AI evaluationを単一のAggregate Metricに潰すことの問題を指摘している&lt;/li&gt;
&lt;li&gt;つまり、SystemとProblemの組み合わせに対するより詳細な評価情報を利用する考え方を議論している&lt;/li&gt;
&lt;li&gt;タイトル自体が「Training on the Test Set」であり、評価データからどこまで情報を抽出してよいかという問題を正面から扱っている&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="sourceとtargetによる分類"&gt;SourceとTargetによる分類&lt;/h2&gt;
&lt;p&gt;以下のような定義になる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Train・Val:
&lt;ul&gt;
&lt;li&gt;Source distributionからサンプリング&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Diag・Test:
&lt;ul&gt;
&lt;li&gt;Target distributionからサンプリング（Diagは見てよい、Testは見てはいけない）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Challenge:
&lt;ul&gt;
&lt;li&gt;Target（デプロイ環境）に関連するが、意図的に分布を歪めて集める&lt;/li&gt;
&lt;li&gt;「独立」というより「Target寄りだが非代表的」という位置づけ&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="本質はinformation-boundary"&gt;本質はInformation Boundary&lt;/h2&gt;
&lt;p&gt;ここで一番重要なのは、Train・Val・Diag・Challenge・Testという名前ではない。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;本質は、どの評価情報を開発プロセスへフィードバックしてよいのかというInformation Boundary&lt;/li&gt;
&lt;li&gt;モデル開発は、Data-&amp;gt;Metric-&amp;gt;Human-&amp;gt;Decision-&amp;gt;Modelというループで進む&lt;/li&gt;
&lt;li&gt;そのため、「モデルのWeight Optimizerに渡していないからTest leakageではない」とは限らない&lt;/li&gt;
&lt;li&gt;人間がTest結果を見てArchitectureを変更すれば、それも立派な情報伝達になる&lt;/li&gt;
&lt;li&gt;したがって評価セットは、まず開発に利用してよいデータと、開発から完全に隔離するデータに分ける必要がある&lt;/li&gt;
&lt;li&gt;その上で開発側を、Parameter Learning・Model Selection・Error Analysis・Capability Analysisに分けると、Train・Val・Diag・Challengeが生まれ、最後にBlind Testを置く、という構造になる&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="まとめ"&gt;まとめ&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;機械学習ではTrain・Val・Testの3分割が基本とされているが、実運用では学習分布と本番分布が一致しないことが珍しくない&lt;/li&gt;
&lt;li&gt;Testで性能が低下した理由を知るにはTarget側のデータを観察する必要があるが、Testを観察して改善に利用すると、そのTestはもはや完全なTestではなくなる&lt;/li&gt;
&lt;li&gt;そこでTrain・Val・Diag・Challenge・Testという役割分離を考える
&lt;ul&gt;
&lt;li&gt;Train: モデルを学習する&lt;/li&gt;
&lt;li&gt;Val: モデルを選択する&lt;/li&gt;
&lt;li&gt;Diag: 本番分布での失敗を理解する診断データセット&lt;/li&gt;
&lt;li&gt;Challenge: 重要なFailure Modeを集中的に診断する&lt;/li&gt;
&lt;li&gt;Test: 最後までBlindにして最終性能を測る&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Source側からTrainとVal、TargetからTest、それと独立したDiagとChallenge&lt;/li&gt;
&lt;li&gt;これはデータ分割のテクニックというより、機械学習開発における情報の流れを設計する問題&lt;/li&gt;
&lt;li&gt;本当に守りたいのは、モデル改善に使った情報と最終評価に使う情報を分離すること&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="参考文献"&gt;参考文献&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://proceedings.mlr.press/v139/koh21a.html" target="_blank" rel="noopener"
&gt;WILDS: A Benchmark of in-the-Wild Distribution Shifts&lt;/a&gt;（Koh et al., ICML 2021）&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://proceedings.mlr.press/v139/chen21i.html" target="_blank" rel="noopener"
&gt;Mandoline: Model Evaluation under Distribution Shift&lt;/a&gt;（Chen et al., ICML 2021）&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://proceedings.mlr.press/v130/subbaswamy21a.html" target="_blank" rel="noopener"
&gt;Evaluating Model Robustness and Stability to Dataset Shift&lt;/a&gt;（Subbaswamy et al., AISTATS 2021）&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://arxiv.org/abs/1907.07174" target="_blank" rel="noopener"
&gt;Natural Adversarial Examples&lt;/a&gt;（Hendrycks et al., CVPR 2021, ImageNet-A）&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://doi.org/10.1609/aaai.v36i11.21487" target="_blank" rel="noopener"
&gt;Training on the Test Set: Mapping the System-Problem Space in AI&lt;/a&gt;（Hernández-Orallo et al., AAAI 2022）&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://proceedings.mlr.press/v119/miller20a.html" target="_blank" rel="noopener"
&gt;The Effect of Natural Distribution Shift on Question Answering Models&lt;/a&gt;（Miller et al., ICML 2020）&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Conformal Predictionによるしきい値調整</title><link>https://www.m1ke.org/p/conformal-prediction%E3%81%AB%E3%82%88%E3%82%8B%E3%81%97%E3%81%8D%E3%81%84%E5%80%A4%E8%AA%BF%E6%95%B4/</link><pubDate>Mon, 24 Aug 2026 12:00:00 +0900</pubDate><guid>https://www.m1ke.org/p/conformal-prediction%E3%81%AB%E3%82%88%E3%82%8B%E3%81%97%E3%81%8D%E3%81%84%E5%80%A4%E8%AA%BF%E6%95%B4/</guid><description>&lt;img src="https://www.m1ke.org/p/conformal-prediction%E3%81%AB%E3%82%88%E3%82%8B%E3%81%97%E3%81%8D%E3%81%84%E5%80%A4%E8%AA%BF%E6%95%B4/graph.png" alt="Featured image of post Conformal Predictionによるしきい値調整" /&gt;&lt;h2 id="背景"&gt;背景&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;しきい値は、多くの場合「なんとなく良さそう」という経験的な決め方になりがち
&lt;ul&gt;
&lt;li&gt;0.9以上ならpositiveと判定する、など&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Conformal Predictionを使うと、しきい値の決め方に統計的な保証を持たせられる&lt;/li&gt;
&lt;li&gt;しきい値調整という実務に絞って、Conformal Predictionの考え方を整理する&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="しきい値をどう決めるかという問題"&gt;しきい値をどう決めるか、という問題&lt;/h2&gt;
&lt;h3 id="経験的なしきい値選定パーセンタイル法"&gt;経験的なしきい値選定（パーセンタイル法）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;よくあるやり方は、キャリブレーション用データ（学習には使っていないホールドアウトデータ）に対してモデルのスコアを計算し、その分布から「これくらいなら安全」というパーセンタイル点をしきい値にする方法&lt;/li&gt;
&lt;li&gt;例えば、negativeサンプルのスコアを集めて、その99パーセンタイル点をしきい値にする、というやり方&lt;/li&gt;
&lt;li&gt;直感的で実装も簡単だが、以下のような弱点がある
&lt;ul&gt;
&lt;li&gt;そのパーセンタイル値が、本番データでも本当に同じ意味を持つのか（同じ誤り率になるのか）の保証がない&lt;/li&gt;
&lt;li&gt;キャリブレーションデータの件数が少ないと、パーセンタイル自体の推定がぶれる&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="経験的な方法の弱点"&gt;経験的な方法の弱点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;「99パーセンタイル」という言葉は、キャリブレーションデータの中では正確でも、未来の新しいデータに対して「エラー率が1%以下になる」ことを保証しない&lt;/li&gt;
&lt;li&gt;特にサンプル数が少ない場合、単純な分位点の推定には誤差が乗る&lt;/li&gt;
&lt;li&gt;この誤差を統計的に補正し、有限サンプルでも成り立つ保証を与えるのがConformal Prediction&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conformal-predictionとは"&gt;Conformal Predictionとは&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;モデルの中身に依存しない（モデルフリーな）枠組みで、予測に対して統計的に妥当な保証を与える手法&lt;/li&gt;
&lt;li&gt;「分布によらない（distribution-free）」という特徴があり、スコアの分布がどんな形であっても、同じ手順で保証が成り立つ&lt;/li&gt;
&lt;li&gt;唯一の前提は、キャリブレーションデータと将来のテストデータが交換可能（exchangeable、i.i.d.なデータもこれに含まれる）であること&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="交換可能性exchangeabilityとは"&gt;交換可能性（exchangeability）とは&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;i.i.d.（独立同分布）は、交換可能性の一種&lt;/li&gt;
&lt;li&gt;i.i.d.なら必ず交換可能だが、逆は成り立たない（交換可能でも、i.i.d.とは限らない）&lt;/li&gt;
&lt;li&gt;交換可能性とは、$n$個のデータ$(x_1, \ldots, x_n)$の同時分布が、順番を入れ替えても変わらないという性質のこと&lt;/li&gt;
&lt;/ul&gt;
$$
P(x_1, \ldots, x_n) = P(x_{\pi(1)}, \ldots, x_{\pi(n)})
$$&lt;ul&gt;
&lt;li&gt;$\pi$は、$1, \ldots, n$のどんな並べ替え（順列）でもよい&lt;/li&gt;
&lt;li&gt;i.i.d.より緩い条件なので、データ同士に依存があっても交換可能性は満たされる場合がある&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以下は、独立ではないが交換可能な例である。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;非復元抽出（sampling without replacement）: 母集団からデータを1件ずつ取り出すとき、取り出したデータを母集団に戻さずに、次の抽出を行う方法
&lt;ul&gt;
&lt;li&gt;例: トランプ52枚から1枚引いて、それを戻さずに次の1枚を引く&lt;/li&gt;
&lt;li&gt;対義語は復元抽出（sampling with replacement）で、こちらは毎回母集団に戻してから次を引く（同じデータが何度でも選ばれうる）&lt;/li&gt;
&lt;li&gt;非復元抽出では、各データの抽出結果はお互いに影響し合う（1枚目でハートのAを引いたら、2枚目でハートのAが出る確率は0になる）ため、独立ではない&lt;/li&gt;
&lt;li&gt;しかし、母集団自体は変わらないので、どの順番で引いても「その$n$枚の組み合わせが出る確率」は同じになる。これが「独立ではないが交換可能」という状態&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Conformal Predictionの証明で重要なのは、「キャリブレーションデータ$n$件＋テストデータ1件」を合わせた$n+1$件が交換可能でありさえすればよい、という点である。i.i.d.である必要はなく、より緩い条件で足りるというのが、この手法の強みの1つ。&lt;/p&gt;
&lt;h3 id="nonconformity-score非適合度スコア"&gt;Nonconformity score（非適合度スコア）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Conformal Predictionでは、まずモデルの出力を「どれだけ普通と違うか（適合していないか）」を表すスコアに変換する&lt;/li&gt;
&lt;li&gt;分類の場合、以下のようなスコアがよく使われる&lt;/li&gt;
&lt;/ul&gt;
$$
s(x, y) = 1 - \hat{p}(y \mid x)
$$&lt;ul&gt;
&lt;li&gt;$\hat{p}(y \mid x)$は、モデルが出力する「入力$x$に対してラベル$y$である確率」&lt;/li&gt;
&lt;li&gt;正解ラベルの予測確率が低いほど、このスコアは大きくなる（＝モデルの予測と実際のラベルが「適合していない」）&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="conformal-predictionによるしきい値の求め方"&gt;Conformal Predictionによるしきい値の求め方&lt;/h2&gt;
&lt;h3 id="キャリブレーションデータでのスコア計算"&gt;キャリブレーションデータでのスコア計算&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;キャリブレーションデータ$n$件それぞれについて、正解ラベルに対するnonconformity score $s_1, \ldots, s_n$を計算する&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="有限サンプル補正つき分位点"&gt;有限サンプル補正つき分位点&lt;/h3&gt;
&lt;p&gt;以下は、目標エラー率$\alpha$（例えば5%）に対応する分位点$\hat{q}$の求め方である。&lt;/p&gt;
$$
\hat{q} = \text{Quantile}\left(\{s_1, \ldots, s_n\}; \ \frac{\lceil (n+1)(1-\alpha) \rceil}{n}\right)
$$&lt;ul&gt;
&lt;li&gt;単純に$(1-\alpha)$分位点を取るのではなく、$\frac{\lceil (n+1)(1-\alpha) \rceil}{n}$という、少しだけ大きい分位点を取っているのがポイント&lt;/li&gt;
&lt;li&gt;この補正が、サンプル数$n$が有限であっても保証が成り立つようにするための調整になっている&lt;/li&gt;
&lt;li&gt;$n$が大きくなるほど、この補正値は$(1-\alpha)$に近づいていく&lt;/li&gt;
&lt;li&gt;同じことだが、$\hat{q}$は「$s_1, \ldots, s_n$を小さい順に並べたとき、$k = \lceil (n+1)(1-\alpha) \rceil$番目に小さい値」と言い換えられる&lt;/li&gt;
&lt;li&gt;次の具体例では、この言い換えの方で計算する&lt;/li&gt;
&lt;li&gt;注意点として、この分位点は一般的な補間つきの分位点（線形補間など）とは一致しない。値と値の間を補間せず、$k$番目の値をそのまま使う（段階的な）分位点である点がポイント&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="しきい値としての解釈"&gt;しきい値としての解釈&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;新しい入力$x$に対して、nonconformity scoreが$\hat{q}$以下となるラベルだけを予測集合に含める、というのが本来のConformal Predictionの出力（予測集合）&lt;/li&gt;
&lt;li&gt;二値分類でFA（誤検知）を抑えたい、という文脈に絞ると、これは「予測確率が、ある値以上ならpositiveとする」という単一のしきい値の形に単純化できる&lt;/li&gt;
&lt;li&gt;つまり、経験的なパーセンタイル法とほぼ同じ計算をしているが、分位点の取り方に前述の有限サンプル補正が入っている点が異なる&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="具体例二値分類でのしきい値計算"&gt;具体例：二値分類でのしきい値計算&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;迷惑メール判定（positive = 迷惑メール）を例に、実際に手で計算してみる&lt;/li&gt;
&lt;li&gt;目標: 本当は迷惑メールではない（negative）メールを誤って迷惑メール判定してしまう率を、20%以下に抑えたい（$\alpha = 0.2$）&lt;/li&gt;
&lt;li&gt;キャリブレーションデータとして、本当はnegativeだと分かっている9件のメールを用意し、それぞれモデルが出す「迷惑メールである確率」を集める&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以下は、9件のキャリブレーションデータのスコア（迷惑メール確率）を小さい順に並べたものである。&lt;/p&gt;
$$
0.02,\ 0.05,\ 0.05,\ 0.08,\ 0.10,\ 0.12,\ 0.15,\ 0.22,\ 0.31
$$&lt;ul&gt;
&lt;li&gt;$n = 9$、$\alpha = 0.2$なので、$k = \lceil (n+1)(1-\alpha) \rceil = \lceil 10 \times 0.8 \rceil = \lceil 8 \rceil = 8$&lt;/li&gt;
&lt;li&gt;つまり、9件のスコアのうち小さい方から8番目の値を$\hat{q}$として使う&lt;/li&gt;
&lt;li&gt;8番目の値は$0.22$なので、$\hat{q} = 0.22$&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これで、しきい値が$0.22$と決まった。新しく届いた3通のメールに対して、モデルが出す迷惑メール確率がそれぞれ$0.10$・$0.25$・$0.40$だったとすると、以下のように判定する。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;$0.10 \leq 0.22$ → negativeと判定（受信）&lt;/li&gt;
&lt;li&gt;$0.25 &gt; 0.22$ → positiveと判定（迷惑メール扱い）&lt;/li&gt;
&lt;li&gt;$0.40 &gt; 0.22$ → positiveと判定（迷惑メール扱い）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;補正の効果を確認するために、同じ9件のスコアに対して、補正なしの単純な80パーセンタイル（線形補間）を計算すると、以下のようになる。&lt;/p&gt;
$$
\text{Quantile}_{0.8}^{\text{naive}} = 0.178
$$&lt;ul&gt;
&lt;li&gt;補正なしだと$0.178$、Conformal Predictionの補正ありだと$0.22$と、後者の方が少し大きい（＝positive判定に慎重になる）しきい値になる&lt;/li&gt;
&lt;li&gt;サンプル数$n=9$が少ないため、この差が目立って出ている。$n$が大きくなるほど、この2つの値は近づいていく&lt;/li&gt;
&lt;li&gt;この差こそが、有限サンプルでも「$\alpha=0.2$以下」という保証を成り立たせるための補正分&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="farとfrrを両方conformal-predictionで保証する"&gt;FARとFRRを両方Conformal Predictionで保証する&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ここまではFA（誤って受理してしまう率、FAR）だけを抑える例だったが、逆側のエラーであるFRR（本来positiveなのに誤って拒否してしまう率）も同時に考えたいことがある&lt;/li&gt;
&lt;li&gt;経験的な方法では、FARとFRRの曲線が交わる点（Equal Error Rate、EER）をしきい値にすることが多いが、これは統計的な保証を持たない、単なる経験的な交点にすぎない&lt;/li&gt;
&lt;li&gt;Conformal Predictionなら、FARとFRRの両方を、それぞれ統計的な保証つきで同時に満たすしきい値を求められる&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="2つのしきい値を別々に求める"&gt;2つのしきい値を別々に求める&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;negative側のキャリブレーションデータから、$\hat{q}_{\text{FAR}}$を求める（$p(x) &gt; \hat{q}_{\text{FAR}}$ならpositive、というルールで$\text{FAR} \leq \alpha_{\text{FA}}$を保証するしきい値）&lt;/li&gt;
&lt;li&gt;positive側のキャリブレーションデータから、$\hat{q}_{\text{FRR}}$を求める（$p(x) \leq \hat{q}_{\text{FRR}}$ならnegative、というルールで$\text{FRR} \leq \alpha_{\text{FR}}$を保証するしきい値）&lt;/li&gt;
&lt;li&gt;どちらも、前述の「有限サンプル補正つき分位点」の手順をそのまま使うが、キャリブレーションに使うデータのクラスが逆になる&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="両立できる条件"&gt;両立できる条件&lt;/h3&gt;
&lt;p&gt;以下が成り立てば、この2つの間のどの$t$を選んでも、両方の保証が同時に成り立つ。&lt;/p&gt;
$$
\hat{q}_{\text{FAR}} \leq \hat{q}_{\text{FRR}}
$$&lt;ul&gt;
&lt;li&gt;理由は、それぞれの保証が単調な集合の包含関係でできているため&lt;/li&gt;
&lt;li&gt;$\text{FAR}(t)$は$t$を上げるほど単調に下がり、$\text{FRR}(t)$は$t$を上げるほど単調に上がる&lt;/li&gt;
&lt;li&gt;$t \geq \hat{q}_{\text{FAR}}$なら、$\{p(x) &gt; t\}$は$\{p(x) &gt; \hat{q}_{\text{FAR}}\}$の部分集合になるので、$\text{FAR}(t) \leq \alpha_{\text{FA}}$がそのまま保たれる&lt;/li&gt;
&lt;li&gt;同様に$t \leq \hat{q}_{\text{FRR}}$なら、$\text{FRR}(t) \leq \alpha_{\text{FR}}$がそのまま保たれる&lt;/li&gt;
&lt;li&gt;つまり、区間$[\hat{q}_{\text{FAR}}, \hat{q}_{\text{FRR}}]$の中でどこに$t$を置いても、どちらの保証も壊れない。むしろ境界から離れるほど、両方に余裕（マージン）が乗る方向に働く&lt;/li&gt;
&lt;li&gt;中間点$t = (\hat{q}_{\text{FAR}} + \hat{q}_{\text{FRR}})/2$を使うのが分かりやすいが、区間内であればどこでもよい&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="両立できない場合"&gt;両立できない場合&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;$\hat{q}_{\text{FAR}} &gt; \hat{q}_{\text{FRR}}$（区間が空）の場合は、$\alpha_{\text{FA}}$と$\alpha_{\text{FR}}$を同時に満たすしきい値が存在しないことを意味する&lt;/li&gt;
&lt;li&gt;この場合、中間を取っても意味がない&lt;/li&gt;
&lt;li&gt;目標のどちらか（あるいは両方）を緩めるか、モデル自体の性能を上げる必要がある、という診断に使える&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="保証されることされないこと"&gt;保証されること・されないこと&lt;/h2&gt;
&lt;h3 id="marginal-coverageの保証"&gt;marginal coverageの保証&lt;/h3&gt;
&lt;p&gt;以下は、Conformal Predictionが与える保証である。&lt;/p&gt;
$$
P\left(y_{\text{test}} \in C(x_{\text{test}})\right) \geq 1 - \alpha
$$&lt;ul&gt;
&lt;li&gt;$C(x_{\text{test}})$は、テスト入力に対する予測集合（しきい値の文脈では「positiveと判定される領域」）&lt;/li&gt;
&lt;li&gt;キャリブレーションデータとテストデータが交換可能であれば、サンプル数に関わらずこの不等式が成り立つ&lt;/li&gt;
&lt;li&gt;「marginal」は、「キャリブレーションデータの取り方（乱数）ごと平均すると」という意味
&lt;ul&gt;
&lt;li&gt;1回のキャリブレーションで固定された$\hat{q}$に対して、個々の入力$x$ごとに条件づけて成り立つ保証（conditional coverage）ではない&lt;/li&gt;
&lt;li&gt;キャリブレーションをやり直すたびに変わる$\hat{q}$も含めて、全体として平均するとこの不等式が成り立つ、という意味&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="なぜ成り立つのか交換可能性と順位の対称性"&gt;なぜ成り立つのか（交換可能性と順位の対称性）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;この保証は近似ではなく、交換可能性（exchangeability）だけから導ける、組み合わせ論的な事実&lt;/li&gt;
&lt;li&gt;キャリブレーションの$n$個のスコア$s_1, \ldots, s_n$と、テストデータのスコア$s_{n+1}$を合わせた$n+1$個が交換可能（どの並び順も同じ確率で起こる）だとする&lt;/li&gt;
&lt;li&gt;このとき、$s_{n+1}$が全体の中で何番目に大きいかは、対称性からどの順位も等確率になる&lt;/li&gt;
&lt;li&gt;したがって、$s_{n+1}$が上位$k$番目以内に入る確率は、ちょうど$\frac{k}{n+1}$になる&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以下のように$k$を選んでおくと、この確率が目標の$1-\alpha$以上になることが保証される。&lt;/p&gt;
$$
k = \lceil (n+1)(1-\alpha) \rceil \implies \frac{k}{n+1} \geq 1 - \alpha
$$&lt;ul&gt;
&lt;li&gt;この議論は、モデルの中身にも、スコアの分布の形にも一切依存していない&lt;/li&gt;
&lt;li&gt;モデルの性能が悪くても、この保証自体は定理として成り立つ（ただし、モデルが悪いとしきい値が緩くなりすぎて、実用上は使い物にならなくなる、という形で影響が出る）&lt;/li&gt;
&lt;li&gt;つまり、保証の強さは丸ごと「交換可能性」という前提に懸かっている。分布シフトなどでこの前提が崩れると、証明も成り立たなくなる&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="条件付きカバレッジは保証されない"&gt;条件付きカバレッジは保証されない&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;marginal coverageは、データ全体で平均した保証であって、クラスやサブグループ単位では保証されない&lt;/li&gt;
&lt;li&gt;例えば、あるサブクラスだけエラー率が目標を大きく超えていても、別のサブクラスでエラー率が低ければ、全体の平均としては目標を満たしてしまうことがある&lt;/li&gt;
&lt;li&gt;対策として、クラスごとに別々にキャリブレーションを行うclass-conditional conformal predictionという拡張がある
&lt;ul&gt;
&lt;li&gt;クラスごとにキャリブレーションデータを分けて、それぞれ別の$\hat{q}$を求める&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="分布シフトで保証が崩れる"&gt;分布シフトで保証が崩れる&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;交換可能性の前提が崩れると（キャリブレーション時と本番運用時でデータの分布が変わると）、保証は成り立たなくなる&lt;/li&gt;
&lt;li&gt;これは、以前書いた分布シフトの問題そのもの&lt;/li&gt;
&lt;li&gt;運用中にデータの性質が変わっていないか、定期的に確認する必要がある&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="実際に必要なサンプル数"&gt;実際に必要なサンプル数&lt;/h2&gt;
&lt;p&gt;キャリブレーションデータの件数$n$は、大きく2段階で考える。&lt;/p&gt;
&lt;h3 id="最低限必要な件数有限であるための下限"&gt;最低限必要な件数（有限であるための下限）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;$k = \lceil (n+1)(1-\alpha) \rceil$が$n$を超えると、$\hat{q}$が存在しなくなる（＝しきい値が無限大になり、全部positive扱いになってしまう）&lt;/li&gt;
&lt;li&gt;この条件から、保証が成り立つための絶対的な下限が以下のように求まる&lt;/li&gt;
&lt;/ul&gt;
$$
n \geq \frac{1}{\alpha} - 1
$$&lt;ul&gt;
&lt;li&gt;例えば$\alpha = 0.05$（目標エラー率5%）なら、最低でも$n \geq 19$必要&lt;/li&gt;
&lt;li&gt;これはあくまで「保証が数学的に成立するための下限」であって、「実用に足る精度」とは別の話&lt;/li&gt;
&lt;li&gt;境界を$\alpha$側から見ると、$\alpha \geq \frac{1}{n+1}$であれば成立し、$\alpha &lt; \frac{1}{n+1}$（厳密に小さい）だと成立しない、という条件になる
&lt;ul&gt;
&lt;li&gt;ちょうど$\alpha = \frac{1}{n+1}$の場合はギリギリ成立し、その場合$k=n$になるので、$\hat{q}$はキャリブレーションデータ中の最大値そのものになる（マージンがない、際どいケース）&lt;/li&gt;
&lt;li&gt;例えば$n=9$なら$\frac{1}{n+1}=0.1$なので、$\alpha=0.1$は成立するが$\alpha=0.09$は不足する&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="実用上必要な件数保証のブレを抑える"&gt;実用上必要な件数（保証のブレを抑える）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;marginal coverageの保証は、どんな$n$でも「平均すれば」成り立つが、1回のキャリブレーションで得られる$\hat{q}$の実際のカバレッジは、$n$が小さいとブレる&lt;/li&gt;
&lt;li&gt;このブレの標準偏差は、キャリブレーションデータの取り方に関するBeta分布の性質から、おおよそ以下のように近似できる&lt;/li&gt;
&lt;/ul&gt;
$$
\text{std}(\text{実際のカバレッジ}) \approx \sqrt{\frac{\alpha(1-\alpha)}{n}}
$$&lt;ul&gt;
&lt;li&gt;具体的な数字を入れると、以下のようになる
&lt;ul&gt;
&lt;li&gt;$\alpha=0.2$、$n=9$（前述の具体例と同じ条件）: std $\approx 13\%$。目標20%に対して、実際は大きくブレうる。手計算で仕組みを説明するには良いが、実用には足りない件数&lt;/li&gt;
&lt;li&gt;$\alpha=0.05$、$n=100$: std $\approx 2.2\%$&lt;/li&gt;
&lt;li&gt;$\alpha=0.05$、$n=1000$: std $\approx 0.7\%$&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;経験則として、$n$が数百〜1000以上あると、実運用で許容できる程度までブレが小さくなることが多い&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="実務上の注意点"&gt;実務上の注意点&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;前述の実装イメージでは&lt;code&gt;negative_scores&lt;/code&gt;だけを使ってキャリブレーションするので、「全サンプル数」ではなく「negativeクラスの件数」がこの$n$に当たる&lt;/li&gt;
&lt;li&gt;class-conditional conformal predictionのように、クラスごとに較正を分ける場合は、クラスごとにこの$n$の条件を満たす必要がある
&lt;ul&gt;
&lt;li&gt;件数が少ないクラス・サブクラスがあると、そこだけ保証が緩くなったり、$\hat{q}$が求まらなくなったりするので注意が必要&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="実装イメージ"&gt;実装イメージ&lt;/h2&gt;
&lt;p&gt;以下は、二値分類でのしきい値調整をConformal Predictionで行う場合の実装イメージである。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;np&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# calib_scores: キャリブレーションデータに対する、モデルのpositive確率&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# calib_labels: キャリブレーションデータの正解ラベル（0 or 1）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# negativeサンプルだけを使って、FA率を抑えるしきい値を求める&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;negative_scores&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;calib_scores&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;calib_labels&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;negative_scores&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;alpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mf"&gt;0.01&lt;/span&gt; &lt;span class="c1"&gt;# 目標エラー率（この場合はFA率）1%&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;q_level&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ceil&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;alpha&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;threshold&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;negative_scores&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;q_level&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;method&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;inverted_cdf&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 本番では、モデルのpositive確率がこのthresholdを超えたらpositiveと判定する&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;q_level&lt;/code&gt;が1を超える場合（サンプル数が少なすぎる場合）は、保証を与えられないケースなので、サンプル数を増やす必要がある&lt;/li&gt;
&lt;li&gt;&lt;code&gt;method=&amp;quot;inverted_cdf&amp;quot;&lt;/code&gt;を指定しているのは、これが前述の「小さい方から$k$番目の値」という定義と厳密に一致する分位点の取り方だから
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;method=&amp;quot;higher&amp;quot;&lt;/code&gt;など、他の補間方法を指定すると、$k$番目の値より大きい値になり、具体例で計算した$\hat{q}$とは異なる結果になってしまう&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="運用での使い方"&gt;運用での使い方&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;キャリブレーションデータは、本番データの分布とずれないよう、定期的に更新して再計算する&lt;/li&gt;
&lt;li&gt;目標エラー率$\alpha$を明示的なパラメータとして持てるため、「FA/hourをこの値以下に抑えたい」といった運用要件に、しきい値選定の手順を直接結びつけやすい&lt;/li&gt;
&lt;li&gt;ただし、marginal coverageの保証はクラス・サブクラス単位では成り立たないため、サブクラスごとに運用上重要な誤り率がある場合は、class-conditionalな較正も検討する&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="まとめ"&gt;まとめ&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;しきい値の経験的な決め方（パーセンタイル法）は、直感的だが将来のデータに対する統計的な保証がない&lt;/li&gt;
&lt;li&gt;Conformal Predictionは、nonconformity scoreの分位点を有限サンプル補正つきで求めることで、分布によらない統計的保証（marginal coverage）を与える&lt;/li&gt;
&lt;li&gt;保証されるのはデータ全体で平均したエラー率であり、クラス・サブグループ単位の保証や、分布シフトへの耐性は別途考える必要がある&lt;/li&gt;
&lt;li&gt;経験的なパーセンタイル法とほぼ同じ手順で計算できるため、既存のしきい値調整の運用に組み込みやすい&lt;/li&gt;
&lt;li&gt;FARとFRRをそれぞれConformal Predictionで較正し、2つのしきい値の間を使えば、EERのような経験的な交点ではなく、両方とも統計的に保証された区間を作れる&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="参考文献"&gt;参考文献&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://qiita.com/udi_/items/8bc1b8c1854340c30216" target="_blank" rel="noopener"
&gt;Conformal Predictionで不確実性を定量化する #機械学習 - Qiita&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://mindfulmodeler.substack.com/p/week-3-conformal-prediction-for-regression" target="_blank" rel="noopener"
&gt;Week #3: Conformal Prediction For Regression&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>機械学習の実験管理の為の設計</title><link>https://www.m1ke.org/p/%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%81%AE%E5%AE%9F%E9%A8%93%E7%AE%A1%E7%90%86%E3%81%AE%E7%82%BA%E3%81%AE%E8%A8%AD%E8%A8%88/</link><pubDate>Fri, 14 Aug 2026 16:28:00 +0900</pubDate><guid>https://www.m1ke.org/p/%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%81%AE%E5%AE%9F%E9%A8%93%E7%AE%A1%E7%90%86%E3%81%AE%E7%82%BA%E3%81%AE%E8%A8%AD%E8%A8%88/</guid><description>&lt;img src="https://www.m1ke.org/p/%E6%A9%9F%E6%A2%B0%E5%AD%A6%E7%BF%92%E3%81%AE%E5%AE%9F%E9%A8%93%E7%AE%A1%E7%90%86%E3%81%AE%E7%82%BA%E3%81%AE%E8%A8%AD%E8%A8%88/exp.png" alt="Featured image of post 機械学習の実験管理の為の設計" /&gt;&lt;h2 id="背景"&gt;背景&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;あるプロジェクトで検知モデルを作っていて色々な課題が出てきた
&lt;ul&gt;
&lt;li&gt;「このrunはどのデータで学習したんだっけ」&lt;/li&gt;
&lt;li&gt;「このmanifestの数字は本当に実行時の設定と一致しているんだっけ」&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;一度、実験管理そのものを設計し直すことにした&lt;/li&gt;
&lt;li&gt;ディレクトリ構成そのものより、そこに至った判断基準の方が汎用的で使い回せる&lt;/li&gt;
&lt;li&gt;考え方の部分だけを抜き出してまとめる&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="全体像"&gt;全体像&lt;/h2&gt;
&lt;p&gt;まず、最終的にどう分割したかを先に示す。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;xxx&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;project&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;scripts&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;scaffold&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="err"&gt;番号採番・テンプレコピーを自動化するスクリプト&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;pipeline&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="err"&gt;データ生成&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="err"&gt;加工の実処理コード（共有、&lt;/span&gt;&lt;span class="n"&gt;experimentごとにコピーしない&lt;/span&gt;&lt;span class="err"&gt;）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;demo&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="err"&gt;実際に動かして触ってみるためのデモスクリプト&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;tools&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="err"&gt;分析・調査に使う単発ツール&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;templates&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="err"&gt;コピーして使う雛形（&lt;/span&gt;&lt;span class="n"&gt;notes&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;manifests&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;docs&lt;/span&gt;&lt;span class="err"&gt;）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;checklist&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;templates&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="err"&gt;を指す&lt;/span&gt;&lt;span class="n"&gt;pointer&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="err"&gt;評価チェック項目（&lt;/span&gt;&lt;span class="n"&gt;eval_checks&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="err"&gt;）の本体&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;specs&lt;/span&gt;&lt;span class="o"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;/&lt;/span&gt; &lt;span class="err"&gt;グレード定義・単語&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="err"&gt;フレーズリスト、シードやレシピみたいなもの&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="o"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;/&lt;/span&gt; &lt;span class="err"&gt;生の長尺音声（未クリップ・未分類、&lt;/span&gt;&lt;span class="n"&gt;collectionsの入力元の1つ&lt;/span&gt;&lt;span class="err"&gt;）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt;&lt;span class="o"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;/&lt;/span&gt; &lt;span class="n"&gt;specs&lt;/span&gt;&lt;span class="err"&gt;（または&lt;/span&gt;&lt;span class="n"&gt;raw&lt;/span&gt;&lt;span class="err"&gt;）から生成&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="err"&gt;収集した実データ&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;splits&lt;/span&gt;&lt;span class="o"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;/&lt;/span&gt; &lt;span class="n"&gt;train&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;test等のロールへの分割&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;aug&lt;/span&gt;&lt;span class="o"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;/&lt;/span&gt; &lt;span class="n"&gt;splitでtrain判定されたクリップへのaug結果&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;/&lt;/span&gt; &lt;span class="err"&gt;埋め込み抽出のキャッシュ&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;│&lt;/span&gt; &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="o"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;/&lt;/span&gt; &lt;span class="err"&gt;評価&lt;/span&gt;&lt;span class="n"&gt;runでFAしたクリップのエラー分析専用データ&lt;/span&gt;&lt;span class="err"&gt;（学習には使わない）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;backlog&lt;/span&gt;&lt;span class="o"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;/&lt;/span&gt; &lt;span class="err"&gt;今すぐ手を付けない課題・気づきを残す場所&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;leaderboard&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;md&lt;/span&gt; &lt;span class="n"&gt;experiment&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;run横断の指標一覧&lt;/span&gt;&lt;span class="err"&gt;（退行チェック用）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;experiments&lt;/span&gt;&lt;span class="o"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;README&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;md&lt;/span&gt; &lt;span class="err"&gt;仮説・作成理由・配下&lt;/span&gt;&lt;span class="n"&gt;runの一覧&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;yaml&lt;/span&gt; &lt;span class="n"&gt;parent_experiment&lt;/span&gt;&lt;span class="err"&gt;・デフォルト設定&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;runs&lt;/span&gt;&lt;span class="o"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;manifest&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;yaml&lt;/span&gt; &lt;span class="err"&gt;実際に何を実行したか（&lt;/span&gt;&lt;span class="n"&gt;cache&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;splits&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;hyperparams&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;seed&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;git&lt;/span&gt; &lt;span class="n"&gt;commit&lt;/span&gt;&lt;span class="err"&gt;）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;├──&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="err"&gt;学習済みモデル等&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;└──&lt;/span&gt; &lt;span class="n"&gt;README&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;md&lt;/span&gt; &lt;span class="n"&gt;run単体の&lt;/span&gt;&lt;span class="err"&gt;「論文」（背景・手法・結果・考察）&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;scripts/&lt;/code&gt;・&lt;code&gt;templates/&lt;/code&gt;・&lt;code&gt;checklist/&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;実験を作るための土台&lt;/li&gt;
&lt;li&gt;採番の自動化・雛形・チェックリスト&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data/&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;学習データの生成・収集パイプライン&lt;/li&gt;
&lt;li&gt;&lt;code&gt;specs → [raw] → collections → splits → aug → cache&lt;/code&gt;の順に積み上がる&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;experiments/&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;実際の実験本体と、その配下の個々のrun&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以下の各節で、この形に至った判断理由を掘り下げる。&lt;/p&gt;
&lt;h2 id="横断設計"&gt;横断設計&lt;/h2&gt;
&lt;h3 id="idの命名規則"&gt;IDの命名規則&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;番号付きフォルダ/ファイルは &lt;code&gt;{ゼロパディング数字}_{slug}&lt;/code&gt; の形式
&lt;ul&gt;
&lt;li&gt;例: &lt;code&gt;01_front_prefix&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;番号は、同じ階層・同じネームスペース内で独立の通番
&lt;ul&gt;
&lt;li&gt;例: &lt;code&gt;checklist/eval_checks/&lt;/code&gt;内の01〜17と、&lt;code&gt;data/specs/&lt;/code&gt;内の01〜は無関係&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;番号を振るもの
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;data/specs/&lt;/code&gt;・&lt;code&gt;data/collections/&lt;/code&gt;・&lt;code&gt;experiments/&lt;/code&gt;・&lt;code&gt;runs/&lt;/code&gt;のように、実際に複数作られていくもの&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;番号を振らないもの
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;templates/notes|manifests|code|docs/&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;種類ごとに1つずつしかない固定スロットなので、増えていく連番ではない&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;checklist/&lt;/code&gt;配下は番号を振る
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;eval_checks/&lt;/code&gt;（相互参照・随時追加のリスト）はもちろん&lt;/li&gt;
&lt;li&gt;&lt;code&gt;notes|manifests|code|docs/&lt;/code&gt;（templates/を指すpointer）も含めて番号を振る&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="idの引用表記namespaceid"&gt;IDの引用表記（namespace#id）&lt;/h3&gt;
&lt;p&gt;IDを文章中で引用する表記は、以下の2通りにした。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;namespace#id&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;例: &lt;code&gt;specs#01_xxx&lt;/code&gt;・&lt;code&gt;experiments#01_baseline&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;#&lt;/code&gt;の左がnamespace（&lt;code&gt;data/specs&lt;/code&gt;のようなフォルダの種類）、右が番号付きslug&lt;/li&gt;
&lt;li&gt;単体で番号が一意に決まるnamespaceに使う&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;namespace#id@parent_namespace#id&lt;/code&gt;
&lt;ul&gt;
&lt;li&gt;例: &lt;code&gt;run#01_xxx@experiment#01_xxx&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;run&lt;/code&gt;のように、親のIDに属する形でしか存在しないnamespaceに使う&lt;/li&gt;
&lt;li&gt;理由は、同じ&lt;code&gt;run#01_xxx&lt;/code&gt;でもどの&lt;code&gt;experiment&lt;/code&gt;配下かで別物になり、親を省略すると取り違えが起きるため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;番号はnamespaceごとに独立の通番なので、&lt;code&gt;01&lt;/code&gt;とだけ書くと文章中ではどのnamespaceの&lt;code&gt;01&lt;/code&gt;か分からなくなる。この表記はディレクトリパスの代わりではなく、あくまで文章中で手短に引用するための省略記法で、実際のパス（&lt;code&gt;data/specs/01_xxx/&lt;/code&gt;等）はコードやmanifestではそのまま使う。この表記があるだけで、読み手が都度パスから逆算しなくて済む。&lt;/p&gt;
&lt;h3 id="なぜと何をを分離する"&gt;「なぜ」と「何を」を分離する&lt;/h3&gt;
&lt;p&gt;実験・run・データの各段階（specs/collections/splits/aug）、それぞれに2つのファイルを持たせる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;README.md&lt;/code&gt; — 仮説・理由・考察など人間が読む文章&lt;/li&gt;
&lt;li&gt;&lt;code&gt;manifest.yaml&lt;/code&gt; — 構造化データ（参照するデータのID・ハイパーパラメータ・git commit等）だけ&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;分離しないとどうなるか？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;理由をmanifestの1行コメントに押し込むと、後から読んでも意図が分からなくなる&lt;/li&gt;
&lt;li&gt;逆に理由の文章に構造化データを混ぜると、スクリプトから機械的に読み取れなくなる&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="rulesとchecklist"&gt;rulesとchecklist&lt;/h3&gt;
&lt;p&gt;プロジェクトの「決め事」を書く場所を、抽象度で2つに分けている。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;rules.md&lt;/code&gt; — 憲法のような、全体を貫く設計方針そのもの
&lt;ul&gt;
&lt;li&gt;なぜこの構造にしたか、判断の理由・トレードオフを文章で説明する&lt;/li&gt;
&lt;li&gt;新しい仕組みを追加する時にまず参照する、判断基準の源&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;checklist/&lt;/code&gt; — 法律のような、個別具体的なチェック項目
&lt;ul&gt;
&lt;li&gt;1項目1ファイルで、都度確認する具体的なルール&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rules.md&lt;/code&gt;の方針を踏まえた上での、実務レベルの確認事項&lt;/li&gt;
&lt;li&gt;理由は、個別に追加・削除・改訂できるようにするため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;理由は、両方を1つのファイルに混ぜると、「そもそもなぜこの設計なのか」という背景と、「今回この項目を確認したか」という実務チェックが同じ場所に混在し、読みたい情報にたどり着きにくくなるため。方針を理解したい時と、作業中に確認したい時とでは、必要な粒度も読むタイミングも違う。&lt;/p&gt;
&lt;p&gt;これは、前述の「README.mdとmanifest.yaml」の分離と同じ「なぜ」と「何を」の軸を、個々のデータ段階だけでなくプロジェクト全体のルールにも適用したものになっている。&lt;/p&gt;
&lt;h2 id="データの設計"&gt;データの設計&lt;/h2&gt;
&lt;h3 id="メタデータ"&gt;メタデータ&lt;/h3&gt;
&lt;p&gt;データのすべてのアイテムには&lt;code&gt;data_item_id&lt;/code&gt;が振られ、&lt;code&gt;role（train/val/test/calibration）&lt;/code&gt;というメタデータで管理される。&lt;/p&gt;
&lt;h3 id="各stageの参照関係"&gt;各stageの参照関係&lt;/h3&gt;
&lt;p&gt;データの生成は基本的に&lt;code&gt;specs → [raw] → collections → splits → aug → cache&lt;/code&gt;の順で積み上がるが、各段階が参照するものは以下の通りで、親を全部知っているわけではない（例: &lt;code&gt;cache&lt;/code&gt;は&lt;code&gt;split&lt;/code&gt;を知らない、&lt;code&gt;splits&lt;/code&gt;はroleをつけるだけで&lt;code&gt;aug&lt;/code&gt;や&lt;code&gt;cache&lt;/code&gt;のことは知らない）。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;stage&lt;/th&gt;
&lt;th&gt;参照するもの&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;specs&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;raw&lt;/td&gt;
&lt;td&gt;specs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;collections&lt;/td&gt;
&lt;td&gt;specs（またはraw）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;splits&lt;/td&gt;
&lt;td&gt;collection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;aug&lt;/td&gt;
&lt;td&gt;collection + split（roleでtrainだけ抜き出すため）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cache&lt;/td&gt;
&lt;td&gt;collection + aug（roleは無視、全部埋め込む）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;run&lt;/td&gt;
&lt;td&gt;cache（複数可）+ split（複数可）——ここで初めてロールと埋め込みが突き合わされる&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="生データのための中間ステージraw"&gt;生データのための中間ステージ（raw）&lt;/h3&gt;
&lt;p&gt;素材によっては、TTS生成や直接録音のように「そのまま1件のクリップになる」形では手に入らないことがある（例: 動画から音声を集める場合、1本が数十分規模の生の長尺音声で、そこから後で複数のクリップを切り出す必要がある）。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;specs → collections&lt;/code&gt;の間に&lt;code&gt;raw&lt;/code&gt;という中間ステージを挟めるようにした（&lt;code&gt;specs → raw → collections&lt;/code&gt;）
&lt;ul&gt;
&lt;li&gt;rawの責務は「未クリップ・未分類の生の長尺音声を保持すること」だけ&lt;/li&gt;
&lt;li&gt;クリップ化やクラス分類は一切行わない、それは引き続き&lt;code&gt;collections&lt;/code&gt;の責務&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;rawを経由するかどうかは素材次第
&lt;ul&gt;
&lt;li&gt;TTS生成・直接録音のような、そのままクリップになる通常の素材はこれまで通りrawを経由しない&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;rawも&lt;code&gt;specs&lt;/code&gt;や&lt;code&gt;collections&lt;/code&gt;と同様にimmutable必須
&lt;ul&gt;
&lt;li&gt;理由は、下流の&lt;code&gt;collections&lt;/code&gt;が参照するため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;こうして「集めた素材の形（そのままクリップか、切り出しが必要な生データか）」による違いを、&lt;code&gt;collections&lt;/code&gt;の責務を汚さずに吸収できるようにしている。&lt;/p&gt;
&lt;h3 id="splitの決め方"&gt;splitの決め方&lt;/h3&gt;
&lt;p&gt;splitは、位置（&lt;code&gt;[::2]&lt;/code&gt;のようなスライス）ではなく、元の&lt;code&gt;data_item_id&lt;/code&gt;に対する決定的ハッシュで分割する。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;同じ&lt;code&gt;data_item_id&lt;/code&gt;なら、何度計算しても同じロールに落ちる
&lt;ul&gt;
&lt;li&gt;理由は、再現性を保つため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;stratify_by_class&lt;/code&gt;（推奨true）
&lt;ul&gt;
&lt;li&gt;collectionのクラスごとに層化してから、roles比率を適用するオプション&lt;/li&gt;
&lt;li&gt;falseだと全&lt;code&gt;data_item_id&lt;/code&gt;を一律にハッシュするだけなので、小さいクラスが特定のロールに偏ってしまうことがある（最悪、あるクラスがtrainに0件になる等）&lt;/li&gt;
&lt;li&gt;層化はロールという概念自体を持たないcollectionではなく、splits側の責務&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="trainvaltestの役割"&gt;train/val/testの役割&lt;/h3&gt;
&lt;p&gt;splitのロールは単に3分割すればよいわけではなく、それぞれ異なる役割を持たせている。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;train — 実際に勾配を流して学習に使う&lt;/li&gt;
&lt;li&gt;val — collectionと同じ生成過程から抜いた、学習には使わない検証用
&lt;ul&gt;
&lt;li&gt;安く頻繁にチェックできる一方、生成過程に共通する癖への過学習を検知できない&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;test — 理想的には生成過程そのものが異なる、独立した「本当の物差し」
&lt;ul&gt;
&lt;li&gt;理由は、同じ生成過程から均等に分けただけだと、trainとtestが同じ癖を共有してしまい、過学習していても高得点が出てしまうため&lt;/li&gt;
&lt;li&gt;別collectionを丸ごとtestにする（&lt;code&gt;roles: {test: 1.0}&lt;/code&gt;）ことでも表現できる&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;必要なら&lt;code&gt;calibration&lt;/code&gt;など、任意のロール名を追加できる&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;valで良い数字が出たからといって、testでの確認を省略しない。&lt;/p&gt;
&lt;h3 id="独立したdataevalステージを作らなかった理由"&gt;独立した&lt;code&gt;data/eval/&lt;/code&gt;ステージを作らなかった理由&lt;/h3&gt;
&lt;p&gt;当初はテスト専用データに独立したステージを設ける案もあったが、結局作らなかった。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;テスト専用データのmanifestは、通常のcollectionとスキーマがほぼ同じで、違いは「学習に使ってよいか」という用途ラベルだけだった
&lt;ul&gt;
&lt;li&gt;独立ステージにすると、ほぼ同じスキーマが2つに分かれてしまう&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;代わりに、splitの&lt;code&gt;roles&lt;/code&gt;を任意個数・任意名に一般化し、&lt;code&gt;roles: {test: 1.0}&lt;/code&gt;という1ロールだけのsplitとして表現することにした
&lt;ul&gt;
&lt;li&gt;理由は、同じcollectionからtrain/val/test/calibrationを1回のsplit定義で切り出す場合と、別の生成過程のcollectionを丸ごとテスト専用にする場合を、同じ仕組みで表現できるため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;代わりに失うものもある
&lt;ul&gt;
&lt;li&gt;独立フォルダという物理的な安全装置（「学習に使うな」を構造で強制する）がなくなるため、命名やレビューといった運用上の注意でカバーする必要がある&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;スキーマを1つ増やす前に、既存の仕組み（この場合はsplitのロール）を一般化できないか考える、という判断の実例になっている。&lt;/p&gt;
&lt;h3 id="処理単位itemchunkbatch"&gt;処理単位（item/chunk/batch）&lt;/h3&gt;
&lt;p&gt;データの最小の処理単位は、大きく3つある。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;item: データのアイテムのファイルの単位（例えば、画像なら1枚の画像）&lt;/li&gt;
&lt;li&gt;chunk: itemをまとめた単位&lt;/li&gt;
&lt;li&gt;batch: itemをまとめた単位&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;chunkはitemをまとめた単位になるため、batch_sizeで割り切れない場合は決定的な結果にならない可能性があるので注意が必要（例えば、BatchNormをかけていたりGPUの丸め誤差など）。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;stage&lt;/th&gt;
&lt;th&gt;単位&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;specs&lt;/td&gt;
&lt;td&gt;item&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;collections&lt;/td&gt;
&lt;td&gt;item&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;splits&lt;/td&gt;
&lt;td&gt;item&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;aug&lt;/td&gt;
&lt;td&gt;item&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;cache&lt;/td&gt;
&lt;td&gt;chunk（&lt;code&gt;.pt&lt;/code&gt;）&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;run&lt;/td&gt;
&lt;td&gt;batch&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="大きい配列のコピーはメモリ量を見積もってチャンク分割する"&gt;大きい配列のコピーはメモリ量を見積もってチャンク分割する&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;array[mask]&lt;/code&gt;や&lt;code&gt;tensor[keep_idx]&lt;/code&gt;のようなfancy indexing（リスト/bool配列でのインデックス参照）は、参照ではなく新しい配列を丸ごと確保してコピーする。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;元データが数万件×埋め込み次元のような大きさの場合、「元の全件」「コピー後の配列」「その後の結合処理の結果」が一時的に同時にメモリへ乗る
&lt;ul&gt;
&lt;li&gt;理由は、fancy indexingが毎回新規確保のコピーを作るため、ピーク時のメモリ使用量が実質2〜3倍になってしまうため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;対策は、一定件数（例: 2万件）ごとのチャンクに分けて、対象範囲へ書き込む形にすること
&lt;ul&gt;
&lt;li&gt;理由は、一時コピーのサイズを小さく抑えられ、ピークメモリを大幅に下げられるため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;大きい配列に対して新しくコピー処理を書くときは、件数×要素サイズがどれくらいのメモリになるか見積もり、必要ならチャンク分割する。&lt;/p&gt;
&lt;h3 id="コピーと参照ミュータブルとイミュータブルの関係"&gt;コピーと参照、ミュータブルとイミュータブルの関係&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;templates/&lt;/code&gt;と&lt;code&gt;data/&lt;/code&gt;は、どちらも「他から使われる元」だが、扱いが正反対になっている。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;テンプレート
&lt;ul&gt;
&lt;li&gt;毎回コピーして使う（experiment/runを作る時点で複製し、そこから先はコピー先が実体になる）&lt;/li&gt;
&lt;li&gt;自由に改訂してよい
&lt;ul&gt;
&lt;li&gt;理由は、コピーした瞬間に元とは切り離され、改訂は過去の実験に影響しないため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;データ（specs・collections・splits・aug）
&lt;ul&gt;
&lt;li&gt;毎回コピーせず、IDによる参照で使う&lt;/li&gt;
&lt;li&gt;新しいバージョンが必要でも上書きせず、番号をincrementした新しいフォルダを切る&lt;/li&gt;
&lt;li&gt;イミュータブルにする
&lt;ul&gt;
&lt;li&gt;理由は、参照元が同じ実体を指し続けるため、元データを書き換えると参照している全experiment/runの再現性が同時に壊れるため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;「コピーか参照か」を分けている基準はコピーのコストで、テンプレートはコストがほぼゼロ、データは数百MB〜数十GBになりうるのでコストが高い。整理すると、コピーは可変でよい、参照は不変でなければならない、という原則になる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;例外: cache（埋め込み抽出のキャッシュ）
&lt;ul&gt;
&lt;li&gt;参照される対象だがイミュータブル必須ではない&lt;/li&gt;
&lt;li&gt;理由は、「collection + aug + 特徴抽出器のバージョン」から機械的に再生成できる純粋な派生物で、壊れても再現でき、失われるのは計算時間だけのため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="immutabilityを物理的に強制するchmod"&gt;immutabilityを物理的に強制する（chmod）&lt;/h3&gt;
&lt;p&gt;「一度作ったら書き換えない」は、最初はドキュメントに書くだけの運用上の約束事だった。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;約束事だけだと、うっかり上書きしてしまう事故を防げない&lt;/li&gt;
&lt;li&gt;そこで、各IDフォルダを、下流から参照され始める時点で読み取り専用にする（&lt;code&gt;chmod -R a-w&lt;/code&gt;）
&lt;ul&gt;
&lt;li&gt;ロックのタイミングは生成した直後ではない
&lt;ul&gt;
&lt;li&gt;理由は、生成〜確定までの間は決定性の検証等で何度も再実行したいことがあるため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;「下流が参照し始める（＝事実上確定する）タイミング」でロックする&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;読み取り専用にしても「削除して作り直す」操作は妨げられない
&lt;ul&gt;
&lt;li&gt;削除はフォルダ側の権限で決まるため、通常の更新フロー（新しいIDを切る）には影響しない&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data/cache/&lt;/code&gt;は対象に含めない
&lt;ul&gt;
&lt;li&gt;理由は、再生成のたびに書き込む必要があるため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ハードリンク（&lt;code&gt;cp -al&lt;/code&gt;）で実体を共有している場合、read-only化は特に重要
&lt;ul&gt;
&lt;li&gt;上書き編集による意図しない連鎖破損を物理的に防げる&lt;/li&gt;
&lt;li&gt;削除の方向は安全（ハードリンクは参照カウント方式なので、他の参照が残っていれば実データは消えない）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="実データはdvcで管理しgitはmanifestだけ追跡する"&gt;実データはDVCで管理し、gitはmanifestだけ追跡する&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;data/&lt;/code&gt;配下の実データ・埋め込みそのものは大容量になりうるため、gitではなくDVCで管理する。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;gitは各段階の&lt;code&gt;manifest.yaml&lt;/code&gt;（と&lt;code&gt;splits&lt;/code&gt;の割り当てcsv）のような軽量なテキストだけを追跡する
&lt;ul&gt;
&lt;li&gt;理由は、実データ自体は数百MB〜数十GBになりうるため、そのままgit管理するとリポジトリが肥大化するため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;実データそのものはDVCでバージョン管理し、manifestが参照するIDとひも付ける
&lt;ul&gt;
&lt;li&gt;理由は、バイナリの実体を専用のデータバージョン管理ツールに任せることで、gitはコードと設定の差分管理に専念できるため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;大きいバイナリと軽量な設定・コードを、同じgit管理の仕組みに混ぜないという判断になっている。&lt;/p&gt;
&lt;h3 id="トップレベルに独立させる理由"&gt;トップレベルに独立させる理由&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;data/&lt;/code&gt;をexperimentの配下ではなく、リポジトリのトップレベルに独立させている。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;理由は、データと実験の関係が1:Nだから
&lt;ul&gt;
&lt;li&gt;1つのcollectionは複数のexperimentから参照されうる&lt;/li&gt;
&lt;li&gt;例: 同じデータで別のモデル構造を試す、同じデータで別のハイパラを試す&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;もしデータをexperiment配下に置くと、実質的に1:1の所有関係になってしまう
&lt;ul&gt;
&lt;li&gt;別のexperimentから使い回すには、コピーするかシンボリックリンクのような回避策が必要になる&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;1:Nの関係を素直に表現するには、データを共有可能な場所（トップレベル）に独立させ、各experimentの&lt;code&gt;manifest.yaml&lt;/code&gt;から「どの&lt;code&gt;data/collections/&amp;lt;id&amp;gt;&lt;/code&gt;を使うか」をIDで参照する形にすればよい。&lt;/p&gt;
&lt;h3 id="specsは目的ごとに分ける"&gt;specsは目的ごとに分ける&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;specs&lt;/code&gt;は「何を集めるか」を定義するtaxonomyだが、同じspecsを複数の目的で兼用しない。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;評価用（実運用に近い性能を測るための物差し）と学習用（モデルの弱点を補強するための素材）のtaxonomyは、扱う現象やカテゴリ名が似ていても、必ず別のspecsとして新設する
&lt;ul&gt;
&lt;li&gt;理由は、評価用specs由来のデータを学習にも使うと、「評価に使うのと同じデータで学習する」ことになり、データリークが起きるため&lt;/li&gt;
&lt;li&gt;評価用taxonomyは学習側から一切参照できないことが前提&lt;/li&gt;
&lt;li&gt;カテゴリの定義が似ていても、収集元（動画・音源）は目的ごとに必ず別に収集し直す&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;判断基準はシンプルで、新しいspecsを作る前に「このtaxonomyは評価用か学習用か」を自問し、両方に使いたくなった場合は1つを共用せず、目的ごとに別のspecsを新設する。&lt;/p&gt;
&lt;h3 id="新しいデータソースはパイロットで検証してから本格展開する"&gt;新しいデータソースはパイロットで検証してから本格展開する&lt;/h3&gt;
&lt;p&gt;新しいデータソースを追加するときは、本格規模で収集する前に必ず少量のパイロットを作り、実際に効果があるか確認してから本格展開する。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;数本〜数十本規模の小さいパイロットをまず収集する&lt;/li&gt;
&lt;li&gt;既存の最良のcache/splitと組み合わせて、ミニマルに再学習・評価する
&lt;ul&gt;
&lt;li&gt;理由は、大きいcollectionを毎回丸ごと作り直さなくても効果検証ができるため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;効果が確認できてから、本格規模の収集を依頼する
&lt;ul&gt;
&lt;li&gt;理由は、パイロットで見えた効果が、規模を変えても同じ方向に働くとは限らないため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;本格展開後も同じ評価をやり直し、効果が維持・拡大しているか確認する
&lt;ul&gt;
&lt;li&gt;理由は、規模を変えると逆効果になるケースがあるため（次項参照）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;小さく検証してから広げるという当たり前の流れだが、パイロットの成功だけで本格投資を決めると、規模を変えたときに効果が消える・悪化するケースを見逃す。&lt;/p&gt;
&lt;h3 id="class-balanced-samplingではグループを分離する"&gt;class-balanced samplingではグループを分離する&lt;/h3&gt;
&lt;p&gt;class単位で重みを正規化するsamplingに新しいデータソースを混ぜる場合、既存の大きいグループにそのまま混ぜず、専用グループに分離するかを検討する。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;既存グループに混ぜると、追加したデータ自身の学習頻度が意図と逆に下がることがある
&lt;ul&gt;
&lt;li&gt;理由は、重みがグループ全体の件数で正規化されるため、グループが大きくなるほど新しく足した分の1件あたりの重みも薄まるため&lt;/li&gt;
&lt;li&gt;結果として、対策のつもりで追加したデータの絶対件数は増えても、実際に学習で使われる頻度はむしろ下がる&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;対策は、追加したデータソースを示すタグを検出し、既存の&lt;code&gt;{class}_{source}&lt;/code&gt;のようなグループとは独立した専用グループとして扱う
&lt;ul&gt;
&lt;li&gt;理由は、他のソースの件数に関係なく、追加分自身の学習頻度を保つため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;新しいデータを追加するときは、「専用グループにするか、既存グループに混ぜるか」を最初に決めておく。既存グループへの混入は、追加する件数が既存グループに対して相対的に大きいほど効果を弱めるため、後から気づきにくい。&lt;/p&gt;
&lt;h3 id="複数ソースをマージする時はコンテンツ重複に注意する"&gt;複数ソースをマージする時はコンテンツ重複に注意する&lt;/h3&gt;
&lt;p&gt;複数のソースからデータをマージしてcollectionを作る場合、パスが違うだけで中身が同一のファイルが紛れ込むことがある。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;本体の一部を間引いた「サブセットビュー」を、別ソースとして安易にマージしない
&lt;ul&gt;
&lt;li&gt;理由は、間引いたものは本体に対して新しい情報を追加しないため、別枠として件数に加算すると二重カウントになるため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;データ移行やマージには&lt;code&gt;cp -al&lt;/code&gt;のようなハードリンクを使う
&lt;ul&gt;
&lt;li&gt;理由は、&lt;code&gt;cp -r&lt;/code&gt;のような複製だと、後から見て「独立ファイルか、元々同じものか」が分からなくなるため&lt;/li&gt;
&lt;li&gt;ハードリンクなら、同一内容が複数箇所にできてしまってもinode照合で低コストに検出できる&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;マージ後は必ずコンテンツ重複の自動チェックを実行する
&lt;ul&gt;
&lt;li&gt;理由は、&lt;code&gt;splits&lt;/code&gt;はdata_item_id単位でハッシュ分割するため、同一データが別IDとして存在すると、同じ内容がtrain/val/testの異なるロールに分裂してしまうため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;複製の混入は気づきにくいが、split後のtrain/testリークという実害につながるため、マージのたびに機械的にチェックする。&lt;/p&gt;
&lt;h3 id="自動抽出のデータには再検証ステップを設計する"&gt;自動抽出のデータには再検証ステップを設計する&lt;/h3&gt;
&lt;p&gt;精度優先の自動抽出（字幕マッチング等）でデータを収集する場合、抽出されなかった残りを別のクラス（例: negative）と決めつけない。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;自動抽出は網羅的ではなく、精度を優先した手法ほど取りこぼしが多い
&lt;ul&gt;
&lt;li&gt;理由は、ヒットしなかったからといって、対象が実際に含まれていないとは限らないため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;「マッチしなかった残りは全部negative」のような推論をする場合は、別途再検証ステップを設計する
&lt;ul&gt;
&lt;li&gt;例: より網羅的な別手法で、対象が本当に含まれていないかを再確認する&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;自動化した抽出パイプラインほど、取りこぼしがそのままラベル誤りとしてデータに混入するリスクがあることを前提にする。&lt;/p&gt;
&lt;h3 id="エラー分析専用のステージerr"&gt;エラー分析専用のステージ（err）&lt;/h3&gt;
&lt;p&gt;評価runで誤発火（FA: False Alarm）したクリップを実際に聴いて原因を調べるため、&lt;code&gt;data/err/&amp;lt;id&amp;gt;/&lt;/code&gt;（&lt;code&gt;manifest.yaml&lt;/code&gt;+&lt;code&gt;README.md&lt;/code&gt;+&lt;code&gt;clips/&lt;/code&gt;）という専用のステージを追加した。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;err由来のクリップは、学習には絶対に使わない
&lt;ul&gt;
&lt;li&gt;理由は、評価専用collectionから切り出したものなので、学習データに混ぜると評価データそのものが学習に漏れてしまうため&lt;/li&gt;
&lt;li&gt;&lt;code&gt;collections&lt;/code&gt;・&lt;code&gt;splits&lt;/code&gt;・&lt;code&gt;aug&lt;/code&gt;・&lt;code&gt;cache&lt;/code&gt;のいずれにもerr由来のクリップを取り込まない&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;分析で見つかったパターンを対策データとして使いたい場合、そのクリップ自体は再利用しない
&lt;ul&gt;
&lt;li&gt;同じパターンの素材を別途独立に収集し直す&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これも、前述の「評価用と学習用を混同しない」という原則の一種になっている。&lt;/p&gt;
&lt;h3 id="生成順序でバグを消すsplitをaugより先に"&gt;生成順序でバグを消す（splitをaugより先に）&lt;/h3&gt;
&lt;p&gt;データ生成のパイプラインを&lt;code&gt;specs → [raw] → collections → splits → aug → cache&lt;/code&gt;の順に固定した。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ポイントは「splitをaugより先に行う」こと
&lt;ul&gt;
&lt;li&gt;先にtrain/val/testに分割する&lt;/li&gt;
&lt;li&gt;train判定されたクリップにだけdata augmentationをかける&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;こうすると、val/testロールのクリップには最初からaugが一切かからなくなる
&lt;ul&gt;
&lt;li&gt;学習コード側に「val/testはaugを除外する」という特別な条件分岐を書く必要がなくなる&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;「気をつけて実装する」ではなく「その状態しか作れないようにする」ことでバグの芽を摘む、という考え方になっている。&lt;/p&gt;
&lt;h2 id="experimentとrunの設計"&gt;experimentとrunの設計&lt;/h2&gt;
&lt;h3 id="分ける理由"&gt;分ける理由&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;experiment
&lt;ul&gt;
&lt;li&gt;実験単位。「何を検証したいか」という仮説・方針のレベル&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;run
&lt;ul&gt;
&lt;li&gt;そのexperimentの中で実際に回した1回1回の試行。ハイパーパラメータ・seed・参照するsplit等の条件を変える&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;1つのexperimentの配下に複数のrunがぶら下がる（&lt;code&gt;experiment:run = 1:N&lt;/code&gt;）&lt;/li&gt;
&lt;li&gt;2階層に分ける
&lt;ul&gt;
&lt;li&gt;理由は、「大きな方針転換」と「同じ方針内の細かい調整」を同じ粒度で並べると、後から見てどちらの変化か読み取れなくなるため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;系譜のリンクも&lt;code&gt;parent_experiment&lt;/code&gt;（方針の系譜）と&lt;code&gt;parent_run&lt;/code&gt;（試行の系譜）で軸を分けている&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="experimentの種類"&gt;experimentの種類&lt;/h3&gt;
&lt;p&gt;「experiment」と一口に言っても、目的によって性質がかなり異なる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;精度向上のための実験 — 実際にモデルを学習し、指標そのものを改善することが目的&lt;/li&gt;
&lt;li&gt;ベースライン実験 — 比較の基準点そのものを作る&lt;/li&gt;
&lt;li&gt;比較実験 — 複数のモデル/手法/設定を同じ条件で横並びに比較する&lt;/li&gt;
&lt;li&gt;アブレーション — ある要素（データソース・機構・ハイパーパラメータ）を1つだけ足し引きし、その要素単体の寄与を切り分ける&lt;/li&gt;
&lt;li&gt;ハイパーパラメータ探索 — 学習率・バッチサイズ等の設定を振って良い設定を探す&lt;/li&gt;
&lt;li&gt;診断的実験 — 「なぜこの現象が起きるか」という原因の特定そのものが目的で、精度向上を直接の目的としない&lt;/li&gt;
&lt;li&gt;エラー分析 — 誤検知したデータを実際に調べて原因を特定する（前述の&lt;code&gt;err&lt;/code&gt;ステージ参照）&lt;/li&gt;
&lt;li&gt;決定性・再現性の検証 — モデルの学習は行わず、パイプラインが決定的かどうかだけを確認する&lt;/li&gt;
&lt;li&gt;汎化性・頑健性の検証 — 生成過程が異なるデータやドメインシフトに対して頑健かを確認する&lt;/li&gt;
&lt;li&gt;評価専用の実験 — 学習は行わず、既存モデルに対して評価パイプラインだけを繰り返し実行する&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;このうち診断的実験・決定性検証・評価専用は、モデルを学習しない点で「精度向上のための実験」とは性質が異なるが、同じ&lt;code&gt;experiment/run&lt;/code&gt;という構造の中にそのまま収まる。仮説・手法・結果・考察という同じ形式で記録できれば十分で、種類ごとに別の入れ物を用意する必要はない。&lt;/p&gt;
&lt;h3 id="コピーで固定化する範囲と共有する範囲を分ける"&gt;コピーで固定化する範囲と、共有する範囲を分ける&lt;/h3&gt;
&lt;p&gt;前述の「コピーのコストで決める」とは別の軸で、コードにも同じコピー/共有の判断が出てくる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;experimentのコード
&lt;ul&gt;
&lt;li&gt;テンプレートからコピーして各experimentフォルダに複製する（共有importにしない）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;データ生成のパイプラインコード（collection.py・splits.py・aug.py・cache.py）
&lt;ul&gt;
&lt;li&gt;コピーせず、リポジトリ全体で共有する&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;判断基準は「それ自体が実験対象かどうか」
&lt;ul&gt;
&lt;li&gt;モデルのコードは実験そのものなので、後からリファクタリングして過去のexperimentの再現性を壊すわけにいかない&lt;/li&gt;
&lt;li&gt;データ生成コードは実験対象ではなく、変わるのは主に引数（単語リストやseed）の方なので共有コードにして育てる&lt;/li&gt;
&lt;li&gt;どのコード状態で生成したかは、各段階のmanifestに&lt;code&gt;git_commit&lt;/code&gt;を自動記録して担保する&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="系譜を2つの軸に分けて追う"&gt;系譜を2つの軸に分けて追う&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;parent_experiment&lt;/code&gt;と&lt;code&gt;parent_run&lt;/code&gt;という、別々の系譜リンクを持たせている
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;parent_experiment&lt;/code&gt; — 研究アイデア・実験方針の系譜（前のexperimentから何を変えたか）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;parent_run&lt;/code&gt; — 同じexperiment内での具体的な試行の系譜（ハイパラを変えた、seedを変えた等）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;最初から軸を分けている
&lt;ul&gt;
&lt;li&gt;理由は、「大きな方針転換」と「同じ方針内の細かい試行錯誤」を1本の履歴に混ぜると、後から辿るときにどちらの変化か分からなくなるため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="運用の自動化"&gt;運用の自動化&lt;/h2&gt;
&lt;h3 id="手作業を自動化して記録と実行のズレを防ぐ"&gt;手作業を自動化して、記録と実行のズレを防ぐ&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;新しいspecs・collection・experiment・runを作るときは、必ず採番用のシェルスクリプト（&lt;code&gt;scaffold_*.sh&lt;/code&gt;）を使う
&lt;ul&gt;
&lt;li&gt;理由は、手でフォルダをコピーして番号を振ると、ID重複や&lt;code&gt;git_commit&lt;/code&gt;書き忘れのようなミスが起きるため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;manifest.yaml&lt;/code&gt;は「事後にまとめたメモ」ではなく、学習・評価スクリプトが実際に読み込む設定そのものとして扱う&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;この2つを徹底すると、「記録されている内容」と「実際に実行された内容」が原理的にズレなくなる。&lt;/p&gt;
&lt;h3 id="チェックリストをフェーズで分けるデータ作成時--評価後"&gt;チェックリストをフェーズで分ける（データ作成時 / 評価後）&lt;/h3&gt;
&lt;p&gt;チェック観点をまとめる&lt;code&gt;checklist/&lt;/code&gt;は、確認するタイミングによって2つのネームスペースに分けている。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;eval_checks/&lt;/code&gt; — 評価runを実行した後に確認する観点
&lt;ul&gt;
&lt;li&gt;クラスバランス・信頼区間・データの不整合・splitのリークなど&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data_checks/&lt;/code&gt; — データを作る/追加する時に確認する観点
&lt;ul&gt;
&lt;li&gt;パイロット先行・グループ分離・コンテンツ重複・immutabilityなど（前述の各項目）&lt;/li&gt;
&lt;li&gt;理由は、評価後に気づくのでは手遅れな観点（データ収集方針そのものの間違い等）を、作る段階でチェックできるようにするため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;どちらも1項目1ファイルで管理し、個別に追加・削除・改訂できるようにしている。&lt;/p&gt;
&lt;h3 id="評価は自動化と人の考察を分ける"&gt;評価は自動化と人の考察を分ける&lt;/h3&gt;
&lt;p&gt;評価観点は&lt;code&gt;checklist/&lt;/code&gt;に1項目1ファイルで管理し、随時追加・改訂できるようにしている。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;客観的に判定できるもの（クラスバランス・サンプル数・信頼区間・データの不整合・splitのリーク）
&lt;ul&gt;
&lt;li&gt;自動テスト化し、runのたびに機械的に実行する&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;解釈が必要なもの（サブクラス間の相関、仮説との差異、汎化の妥当性）
&lt;ul&gt;
&lt;li&gt;自動化せず、run単体のREADME.mdに人が文章で書く&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;横断的な良化/悪化の比較は&lt;code&gt;leaderboard.md&lt;/code&gt;に1行ずつ追記していく
&lt;ul&gt;
&lt;li&gt;理由は、退行に気づけるようにするため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;何でも自動化しようとせず、「機械的に白黒つけられるか」で線引きする。&lt;/p&gt;
&lt;h3 id="対策より先に原因を特定するエラー分析のワークフロー"&gt;対策より先に、原因を特定する（エラー分析のワークフロー）&lt;/h3&gt;
&lt;p&gt;指標が悪化した/改善しないと分かった時点で、いきなり「データを増やす」対策に飛びつかず、まず原因を特定するところまでを標準の手順にした。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;誤検知したデータを専用ステージ（&lt;code&gt;err&lt;/code&gt;）に抽出する
&lt;ul&gt;
&lt;li&gt;学習には混ぜない（前述の通り）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;聴く・見る・文字起こしするなどして、内容面での共通点を探す&lt;/li&gt;
&lt;li&gt;テキストだけで説明できない場合、モデルの特徴量空間でnearest neighbor分析を行う
&lt;ul&gt;
&lt;li&gt;理由は、声質やチャンネルの偏りのような、言語化しにくい非言語的な要因を検出するため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;原因に対してピンポイントな対策データを設計し、同じ評価データで効果を検証する&lt;/li&gt;
&lt;li&gt;改善しなければ、同じ手順を繰り返す
&lt;ul&gt;
&lt;li&gt;理由は、原因の特定が不十分なまま対策を打っても、的外れな対策になるか、既存の仕組み（class-balanced samplingのグループ分け等）との相互作用で意図しない逆効果になりうるため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;反復的な調査記録は、モデル学習を行わない検証専用のexperimentとして残す。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;理由は、通常のexperimentと同じ形式（README・manifest）で調査の系譜を追えるようにするため&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;数値の良化/悪化だけを見て次のパラメータを試すやり方では、ラベル誤りや学習データの偏りに起因するショートカット学習のような根本原因までは見つからない。&lt;/p&gt;
&lt;h3 id="backlog課題を残す場所"&gt;backlog（課題を残す場所）&lt;/h3&gt;
&lt;p&gt;作業中に気づいた課題や違和感を、その場で対応するかどうか判断せずに、まず&lt;code&gt;backlog/&amp;lt;id&amp;gt;/README.md&lt;/code&gt;として残す。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;IDの命名規則は他の番号付きnamespaceと同じ（&lt;code&gt;{ゼロパディング数字}_{slug}&lt;/code&gt;）
&lt;ul&gt;
&lt;li&gt;引用表記も&lt;code&gt;backlog#01_xxx&lt;/code&gt;のように使える&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;目的は「今すぐ着手しない」と「忘れる」を分けること
&lt;ul&gt;
&lt;li&gt;逐一対応すると本来の作業が進まない、その場限りのメモだと後で誰も覚えていない&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;README.md&lt;/code&gt;だけの軽量な形
&lt;ul&gt;
&lt;li&gt;理由は、&lt;code&gt;data/&lt;/code&gt;や&lt;code&gt;experiments/&lt;/code&gt;のように厳密なmanifestやimmutabilityを必要としないため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;あくまで「後で読み返して着手判断する」ためのメモであり、何かを参照される実体ではない。&lt;/p&gt;
&lt;h2 id="決定性再現性の検証"&gt;決定性・再現性の検証&lt;/h2&gt;
&lt;h3 id="安定化モードの例"&gt;安定化モードの例&lt;/h3&gt;
&lt;p&gt;以下が安定化コードの例で、runの&lt;code&gt;manifest.yaml&lt;/code&gt;などに&lt;code&gt;seed: 42&lt;/code&gt;のような値を埋めて使う。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;experimentを作るときにコピーして使う「安定化コード」(安定化モード)——学習の再現性
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;(reproducibility)を保証するための決定論設定。共有importにせず、experimentごとにコピーして
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;始める(過去のexperimentの再現性を守るため。詳細はrules.mdの`code/`節参照)。
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;random&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;numpy&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nn"&gt;np&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;torch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manual_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cuda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manual_seed_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;enable_determinism&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;runの再現性を検証するための厳格決定論モード。TF32・cudnn自動チューニング・
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; 非決定的なCUDA kernelを全て無効化する(有効なままだとhardwareや実行順で結果が
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; 僅かに揺れ、seed固定だけでは同一runの再現性を保証できない)。strict=Trueだと
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; 決定論的実装が無いopが例外で落ちる(warn_only=False)。&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cudnn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deterministic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cudnn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;benchmark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cuda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;matmul&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;allow_tf32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cudnn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;allow_tf32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;use_deterministic_algorithms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warn_only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;use_deterministic_algorithms(strict=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;strict&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, warn_only=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;strict&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) &amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;+ TF32 無効化 + cudnn.deterministic=True&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="非決定性は下流に伝播するためパイプラインの全段階を検証する"&gt;非決定性は下流に伝播するため、パイプラインの全段階を検証する&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;specs → [raw] → collections → splits → aug → cache&lt;/code&gt;のどこか1段階でも非決定的な処理が混ざると、それ以降の段階は全て非決定的になる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;各段階を個別に複数回re-runし、byte-for-byteで同一の結果になるか確認する
&lt;ul&gt;
&lt;li&gt;理由は、後段（例: cache）だけを確認しても、非決定性がどの段階で混入したかは特定できないため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;自前のコードだけでなく、使っている外部ライブラリ（音声処理・特徴抽出器等）が内部で非決定的な処理（並列化・GPU上の演算順序等）を行っていないかも確認する&lt;/li&gt;
&lt;li&gt;処理順序を変えて確認する（元の順・逆順・並列実行）
&lt;ul&gt;
&lt;li&gt;理由は、浮動小数点演算は結合則が成り立たないため、足し合わせる順序やバッチの構成が変わるだけで結果がわずかにブレることがあり、順序を変えて初めて顕在化するため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;1段階でも見落とすと、その後どれだけ丁寧に決定性を検証しても意味がなくなる。&lt;/p&gt;
&lt;h3 id="最初にやるべき実験"&gt;最初にやるべき実験&lt;/h3&gt;
&lt;p&gt;最初にやるべきなのは、確実に決定的になるかの試験と信頼区間を出すこと。実験の全ての前提は、データと実験の安定性にかかっている。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;データのsplitが決定的になるかを確認する&lt;/li&gt;
&lt;li&gt;cacheを作る時にcacheが決定的になるかを確認する
&lt;ul&gt;
&lt;li&gt;特に高速化のために並列処理を入れると、決定的にならないことがある&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;安定化コードを入れて、同じデータで学習を複数回行い、byte-for-byteレベルで決定的な結果になるかを確認する&lt;/li&gt;
&lt;li&gt;それらを複数回評価して、評価コード自体が決定的かどうかを確認する&lt;/li&gt;
&lt;li&gt;seedを変えたときのばらつき、dropoutを入れたときのばらつきを見る&lt;/li&gt;
&lt;li&gt;決定的にならなかった場合は、クラス・サブクラス・グレードなどの区分でどれくらい信頼区間があるかを確認する&lt;/li&gt;
&lt;li&gt;ソートも一意になるように第二・第三ソートキーまで指定する&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;結局のところ、seed値・データ・キャッシュ・学習・評価のどこから不確実性が入り、再現性が取れなくなるのかを明らかにする必要がある。CUDAの設定を決定論的にしていても、キャッシュレベルで不確実性が入っていることがあるので注意が必要。&lt;/p&gt;
&lt;h3 id="実例cacheの決定性チェックで見つかったバグ"&gt;実例：cacheの決定性チェックで見つかったバグ&lt;/h3&gt;
&lt;p&gt;実際に決定性チェック用の実験（&lt;code&gt;experiments/02_cache_determinism&lt;/code&gt;）を作って検証したところ、2つのバグが見つかった。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fit_window&lt;/code&gt;のjitterが共有rng依存になっていて、非決定的だった&lt;/li&gt;
&lt;li&gt;GPU上でのバッチ構成に依存して、浮動小数点の計算結果がブレていた&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;対策として、&lt;code&gt;cache&lt;/code&gt;の&lt;code&gt;manifest.yaml&lt;/code&gt;に以下を明示的に持たせて固定・記録するようにした。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;seed&lt;/code&gt;（ハッシュ導出の種）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;chunk_size&lt;/code&gt;（チェックポイント区切り）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;batch_size&lt;/code&gt;（特徴抽出器へ一度に渡す件数。バッチ構成がGPUの計算順序に影響するため、固定・記録が必要）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;「関数呼び出し時の引数では気軽に変えられないようにする」ことで、後から知らないうちに条件が変わってしまうのを防いでいる。この実例が示すのは、決定性は「CUDAの設定を決定論的にすれば十分」ではなく、キャッシュ生成のような一見地味な処理にも不確実性が紛れ込みうるということ。&lt;/p&gt;
&lt;h3 id="並列処理でも決定的にする共有rngを使わない"&gt;並列処理でも決定的にする（共有RNGを使わない）&lt;/h3&gt;
&lt;p&gt;同じseedを使っていても、並列処理のworker数や処理順序が変わると結果がズレることがある。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;原因は、複数のworkerが1つの共有RNG（乱数生成器）を順番に消費する実装になっていると、どのアイテムがどのタイミングで乱数を引くかがworker数やスケジューリングに依存してしまうため&lt;/li&gt;
&lt;li&gt;対策は、各アイテムに固有の決定的なseedを、そのアイテムのIDから導出すること
&lt;ul&gt;
&lt;li&gt;例: &lt;code&gt;hash(seed, data_item_id)&lt;/code&gt;のように、グローバルなseedとアイテムのIDを合わせてハッシュ化し、そのアイテム専用のseedとして使う&lt;/li&gt;
&lt;li&gt;理由は、この方式ならどのworkerがいつそのアイテムを処理しても、同じアイテムには常に同じ乱数列が使われるため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;jitter付与・ランダムクロップなど、並列実行の順序やworker数に依存しうる処理はこの方式に置き換えて検証する&lt;/li&gt;
&lt;li&gt;外部ライブラリを使う場合も、呼び出し直前にアイテム固有のseedでローカルにreseedすれば、ライブラリ内部の実装に関わらず同じ決定性を確保できる
&lt;ul&gt;
&lt;li&gt;理由は、ライブラリ側の乱数状態を外から一時的に固定してしまえば、そのライブラリ自体が共有RNGを使っていても影響を受けないため&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;「seedを固定する」だけでは不十分で、「どこからその都度の乱数を引いているか」まで見ないと、並列化した瞬間に再現性が崩れる。これは特定のライブラリの機能ではなく、状態を持たない乱数の一般的な作法で、JAXの&lt;code&gt;PRNGKey&lt;/code&gt;（内部にグローバルな状態を持たず、明示的に受け渡す純粋関数型の乱数生成）・NumPyの&lt;code&gt;SeedSequence.spawn()&lt;/code&gt;・PyTorch DataLoaderのworkerごとのseedingも、同じ発想の実装になっている。&lt;/p&gt;
&lt;h2 id="実験のcheckability"&gt;実験のCheckability&lt;/h2&gt;
&lt;p&gt;作業は、特に中間生成物を残させるようにしなければならない。例えばWhisperで作業を数時間かけてもらう時は、必ず次の3つのことをやる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;小さなセットで事前にdry-run&lt;/li&gt;
&lt;li&gt;中間生成物（Whisperの結果）はファイルに逐次保存&lt;/li&gt;
&lt;li&gt;他のAgentにDouble Checkしてもらう&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Whisperの処理には時間がたくさんかかり、数時間も待つ羽目になるため、中間生成物は必ずファイルに保存しておく。こうすることで、リトライや再検証などができるようになる。&lt;/p&gt;
&lt;h2 id="まとめ"&gt;まとめ&lt;/h2&gt;
&lt;p&gt;実験管理を設計し直す中で、ディレクトリ構成そのものより「なぜそう分けたか」という判断基準の方が、他のプロジェクトにも使い回せると感じた。挙げた基準は、次の通り。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;方針転換（experiment）と細かい試行（run）を別の粒度として分ける&lt;/li&gt;
&lt;li&gt;理由（文章）と構造化データを別ファイルに分ける&lt;/li&gt;
&lt;li&gt;コピーは可変でよく、参照は不変でなければならない、という原則でミュータブル/イミュータブルを線引きする&lt;/li&gt;
&lt;li&gt;コピーか参照かは、コピーのコストで決める（安ければコピー、高ければ参照）&lt;/li&gt;
&lt;li&gt;「書き換えない」という約束事は、ドキュメントに書くだけでなく&lt;code&gt;chmod&lt;/code&gt;で物理的に強制する&lt;/li&gt;
&lt;li&gt;1:Nで参照される対象は、所有関係を1:1にしないようトップレベルの共有領域に独立させる&lt;/li&gt;
&lt;li&gt;気をつけて実装するのではなく、間違った状態そのものを作れなくする（splitをaugより先に等）&lt;/li&gt;
&lt;li&gt;スキーマを増やす前に、既存の仕組みを一般化できないか考える&lt;/li&gt;
&lt;li&gt;実験対象のコードは複製して固定化し、実験対象でないコードは共有して育てる&lt;/li&gt;
&lt;li&gt;手作業を自動化し、記録と実行がズレない構造にする&lt;/li&gt;
&lt;li&gt;自動化できる評価と、人が書くべき考察を最初から切り分ける&lt;/li&gt;
&lt;li&gt;新しいデータは小さく検証してから広げ、指標が悪化したら原因の特定まで追う&lt;/li&gt;
&lt;li&gt;「気をつけて運用する」のではなく、間違いにくい状態しか作れないようにする（scaffoldスクリプト・chmod等）&lt;/li&gt;
&lt;li&gt;特に全てのコードをCheckするのは無理なので、道を外さず、方向性を間違わないようにガードレースを作っていくイメージ&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;どれも当たり前に見えるが、後から気づいて直すのは大変。次に新しいプロジェクトを始めるときも、同じ基準で最初から作っていきたい。また、もっとやる場合は、そもそも&lt;code&gt;xxx-project-data&lt;/code&gt;と&lt;code&gt;xxx-project-train&lt;/code&gt;とかに分けてもいいのかもしれない。&lt;/p&gt;</description></item><item><title>分布外データを含む分類でFalse Positiveを抑える方法</title><link>https://www.m1ke.org/p/%E5%88%86%E5%B8%83%E5%A4%96%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E5%90%AB%E3%82%80%E5%88%86%E9%A1%9E%E3%81%A7false-positive%E3%82%92%E6%8A%91%E3%81%88%E3%82%8B%E6%96%B9%E6%B3%95/</link><pubDate>Wed, 22 Jul 2026 10:52:57 +0900</pubDate><guid>https://www.m1ke.org/p/%E5%88%86%E5%B8%83%E5%A4%96%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E5%90%AB%E3%82%80%E5%88%86%E9%A1%9E%E3%81%A7false-positive%E3%82%92%E6%8A%91%E3%81%88%E3%82%8B%E6%96%B9%E6%B3%95/</guid><description>&lt;img src="https://www.m1ke.org/p/%E5%88%86%E5%B8%83%E5%A4%96%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E5%90%AB%E3%82%80%E5%88%86%E9%A1%9E%E3%81%A7false-positive%E3%82%92%E6%8A%91%E3%81%88%E3%82%8B%E6%96%B9%E6%B3%95/distribution.jpg" alt="Featured image of post 分布外データを含む分類でFalse Positiveを抑える方法" /&gt;&lt;h2 id="背景"&gt;背景&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;機械学習モデルを利用したシステムの開発で、precisionを高める必要があった&lt;/li&gt;
&lt;li&gt;特に、対象ではないデータをpositiveと誤判定するFalse Positive（FP）のコストが高かったた&lt;/li&gt;
&lt;li&gt;そのため、評価データ上でFPを発生させず、可能な限り高いrecallやcoverageを維持することを目標にした&lt;/li&gt;
&lt;li&gt;しかし、本番環境では入力がフリーフォームに近く、事前にすべての入力パターンを列挙したり、完全に制限したりすることは難しい&lt;/li&gt;
&lt;li&gt;なので、学習時に想定していなかった未知のデータ、すなわち分布外データに対する対策が必要になった&lt;/li&gt;
&lt;li&gt;ここでは、そのときに実施した対策と、それぞれの限界についての備忘録をまとめる&lt;/li&gt;
&lt;li&gt;なお、有限の評価データでFPが0件だったとしても、将来のあらゆる入力に対してFPが発生しないことを保証できるわけではない点に注意&lt;/li&gt;
&lt;li&gt;ここでの「FPを抑える」とは、モデルとシステムの両面から誤判定のリスクを下げることを意味する&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="用語"&gt;用語&lt;/h2&gt;
&lt;h3 id="ラベル"&gt;ラベル&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;positive
&lt;ul&gt;
&lt;li&gt;判定対象であるデータに付ける正例ラベル&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;negative
&lt;ul&gt;
&lt;li&gt;判定対象ではないデータに付ける負例ラベル&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="idとood"&gt;IDとOOD&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ID（In-Distribution）
&lt;ul&gt;
&lt;li&gt;学習時および運用時に想定している分布に属するデータ&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;OOD（Out-of-Distribution）
&lt;ul&gt;
&lt;li&gt;学習時に想定した分布から外れるデータ&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;疑似OOD
&lt;ul&gt;
&lt;li&gt;外部データやデータ加工によって作成した、OODを模したデータ&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;NOTE:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ID/OODとpositive/negativeは別の概念&lt;/li&gt;
&lt;li&gt;ID/OODは「想定した分布に属するか」を表し、positive/negativeは「判定対象であるか」を表す&lt;/li&gt;
&lt;li&gt;したがって、IDのnegativeやOODのpositiveが存在する可能性もある&lt;/li&gt;
&lt;li&gt;実装上、OODをnegativeまたはNoneとして扱う場合があるが、概念として同一ではない&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="対象と想定の4象限"&gt;対象と想定の4象限&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;-&lt;/th&gt;
&lt;th&gt;対象（Positive）&lt;/th&gt;
&lt;th&gt;非対象（Negative／None）&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;想定内（ID）&lt;/td&gt;
&lt;td&gt;① ID-Positive&lt;/td&gt;
&lt;td&gt;② ID-Negative&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;想定外（OOD）&lt;/td&gt;
&lt;td&gt;③ OOD-Positive&lt;/td&gt;
&lt;td&gt;④ OOD-Negative&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;None：ラベル上、対象クラスではない&lt;/li&gt;
&lt;li&gt;OOD：学習・想定分布から外れている&lt;/li&gt;
&lt;li&gt;ID-Posivtive：想定範囲内の対象&lt;/li&gt;
&lt;li&gt;ID-Negative：想定範囲内の非対象&lt;/li&gt;
&lt;li&gt;OOD-Negative：想定範囲外の非対象&lt;/li&gt;
&lt;li&gt;OOD-Positive：分布外だが、本当は対象に該当する&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="mining"&gt;Mining&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;mining
&lt;ul&gt;
&lt;li&gt;大量の候補から、学習に役立つデータを発掘すること&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;hard negative mining
&lt;ul&gt;
&lt;li&gt;現在のモデルがpositiveと誤判定しやすい負例を優先的に発掘すること&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="ood検出"&gt;OOD検出&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;入力が学習時に想定した分布の範囲内か、それとも分布外かを判定すること&lt;/li&gt;
&lt;li&gt;一般には、モデルや特徴量からOODスコアを算出し、しきい値を超えた入力をOODとして扱う&lt;/li&gt;
&lt;li&gt;ただし、IDとOODの境界は絶対的なものではなく、「何を想定範囲とするか」というタスクの定義にも依存する&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="iid独立同分布"&gt;i.i.d.（独立同分布）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;データが互いに独立であり、同じ確率分布に従うという仮定のこと&lt;/li&gt;
&lt;li&gt;機械学習では、学習データと評価・運用データが同じ分布から生成されるという仮定を置くことが多い&lt;/li&gt;
&lt;li&gt;現実の運用環境では、時間、ユーザー、センサー、入力方法などの変化によって、この仮定が崩れることがある&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="内挿と外挿"&gt;内挿と外挿&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;内挿（interpolation）
&lt;ul&gt;
&lt;li&gt;学習データが十分に存在する領域における予測&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;外挿（extrapolation）
&lt;ul&gt;
&lt;li&gt;学習データがほとんど存在しない領域における予測&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;学習域
&lt;ul&gt;
&lt;li&gt;学習データが分布している領域を指す非形式的な表現&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;多次元データでは、各特徴量が単独では学習時の範囲内でも、特徴量の組み合わせとしては学習データから大きく外れていることがある。&lt;/p&gt;
&lt;p&gt;なお、外挿とOODは関係が深いが、必ずしも同じ概念ではない。&lt;/p&gt;
&lt;h3 id="バイアス"&gt;バイアス&lt;/h3&gt;
&lt;p&gt;バイアスとは、調査や分析結果が、真の値や関係性から体系的にずれてしまうこと。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;バイアスの種類&lt;/th&gt;
&lt;th&gt;定義&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;交絡バイアス&lt;/td&gt;
&lt;td&gt;第3の因子が介在し、見かけ上の因果関係を生む&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;選択バイアス&lt;/td&gt;
&lt;td&gt;調査対象の選定に偏りがあり、標本が母集団を代表しない&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;情報バイアス&lt;/td&gt;
&lt;td&gt;測定や回答、記録などの不正確さによってデータに偏りが生じる&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="因果関係と相関関係"&gt;因果関係と相関関係&lt;/h3&gt;
&lt;p&gt;ココらへんも頻出なので注意。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;観点&lt;/th&gt;
&lt;th&gt;相関関係&lt;/th&gt;
&lt;th&gt;因果関係&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;意味&lt;/td&gt;
&lt;td&gt;AとBが一緒に変化する&lt;/td&gt;
&lt;td&gt;AがBを引き起こす&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;関係&lt;/td&gt;
&lt;td&gt;「Aが増えるとBも増える／減る」&lt;/td&gt;
&lt;td&gt;「Aを変えると、その結果Bが変わる」&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;原因・結果&lt;/td&gt;
&lt;td&gt;わからない&lt;/td&gt;
&lt;td&gt;原因と結果がある&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;方向性&lt;/td&gt;
&lt;td&gt;基本的に方向は決まらない&lt;/td&gt;
&lt;td&gt;A → Bという方向がある&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;第三の要因&lt;/td&gt;
&lt;td&gt;影響している可能性がある&lt;/td&gt;
&lt;td&gt;交絡などを排除する必要がある&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;データだけで判断できる？&lt;/td&gt;
&lt;td&gt;比較的見つけやすい&lt;/td&gt;
&lt;td&gt;相関だけでは証明できない&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;例&lt;/td&gt;
&lt;td&gt;身長と体重には相関がある&lt;/td&gt;
&lt;td&gt;喫煙は肺がんリスクを高める&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id="欠落と交絡"&gt;欠落と交絡&lt;/h3&gt;
&lt;p&gt;早とちりの論理は多いので注意。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;欠落
&lt;ul&gt;
&lt;li&gt;重要な第三の変数を落とした（欠落変数）上の結論&lt;/li&gt;
&lt;li&gt;特に、その変数との交互作用を無視した結論&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;何を分析から落としたかという問題&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;例:
&lt;ul&gt;
&lt;li&gt;「モデルのパラメーターを増やしたら=&amp;gt;前回より精度が落ちた」 ∴ 「モデルのパラメータを増やすと精度が落ちる」&lt;/li&gt;
&lt;li&gt;でも実際はデータを増やしたら（第三の変数）、結果は良くなった&lt;/li&gt;
&lt;li&gt;つまり「Bを固定したときのAの効果」を「Aの普遍的な効果」だと思ってしまった&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;交絡（こうらく）
&lt;ul&gt;
&lt;li&gt;第三の因子による見せかけ上の因果関係を出す事&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;欠落によって因果推定がどう歪んだかという構造&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;例:
&lt;ul&gt;
&lt;li&gt;「アイスが売れると水難事故が増える」&lt;/li&gt;
&lt;li&gt;実際は気温が関係ある&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="タクソノミーtaxonomy"&gt;タクソノミー（Taxonomy）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;対象世界を、どんな粒度・軸・関係性で分類するかを体系立てて定義したもの&lt;/li&gt;
&lt;li&gt;機械学習のデータ設計では、クラス・サブクラス・grade（難易度）などの階層構造として現れることが多い&lt;/li&gt;
&lt;li&gt;タクソノミーを先に定義してからデータを集めることで、データセットの完全性・無矛盾性を担保しやすくなる（後述の「seedが実験の仮説」も参照）&lt;/li&gt;
&lt;li&gt;タクソノミーに漏れがあると、学習・評価どちらでもカバーできない領域が生まれ、それがOODの一因にもなる&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;NOTE:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ただし、実験計画法の要因実験のように、軸・粒度をすべてマニュアルで組み合わせて定義しようとすると、組み合わせ爆発でパターン数が破綻しやすい&lt;/li&gt;
&lt;li&gt;そのため、全パターンを人手で列挙するのではなく、実データから頻出パターンを抽出してタクソノミーを組み立てる方が現実的&lt;/li&gt;
&lt;li&gt;例えば、ある言葉の使われ方の前後の文脈を助詞レベルで定義するのではなく、Wikiからデータを抽出してFSMを定義するなどのほうがいい&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="ゴールドラベルとシルバーラベル"&gt;ゴールドラベルとシルバーラベル&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;ゴールド（Gold）
&lt;ul&gt;
&lt;li&gt;人の手で検証・確認されたラベル&lt;/li&gt;
&lt;li&gt;品質は高いが、作成コストが高く量を確保しにくい&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;シルバー（Silver）
&lt;ul&gt;
&lt;li&gt;モデルやルールベース処理などで自動生成されたラベル&lt;/li&gt;
&lt;li&gt;量は確保しやすいが、ゴールドに比べてノイズが混ざりやすい&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;一般的に、シルバーで量を確保しつつ、一部をゴールドで検証・補強するという使い分けをする&lt;/li&gt;
&lt;li&gt;ただし、シルバーは往々にして間違っている事が多いので、注意&lt;/li&gt;
&lt;li&gt;評価データは特にゴールドで揃えることが望ましく、シルバーのまま評価に使うと、モデルの精度なのかラベルのノイズなのかが区別できなくなる&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="分布シフト"&gt;分布シフト&lt;/h2&gt;
&lt;h3 id="機械学習モデルの前提と問題"&gt;機械学習モデルの前提と問題&lt;/h3&gt;
&lt;p&gt;教師あり機械学習モデルは一般に、学習データから得た統計的な関係が評価時や運用時にも成立することを期待して学習される。&lt;/p&gt;
&lt;p&gt;しかし、現実の運用環境では次のような理由でデータ分布が変化する。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;データの収集方法が変わる&lt;/li&gt;
&lt;li&gt;ユーザー層が変わる&lt;/li&gt;
&lt;li&gt;撮影環境やセンサーが変わる&lt;/li&gt;
&lt;li&gt;時間の経過によって対象の性質が変わる&lt;/li&gt;
&lt;li&gt;学習データの選び方に偏りがある&lt;/li&gt;
&lt;li&gt;データ生成メカニズムそのものが変わる&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;このような学習時と運用時の分布の違いを、分布シフトと呼ぶ。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.m1ke.org/p/%E5%88%86%E5%B8%83%E5%A4%96%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E5%90%AB%E3%82%80%E5%88%86%E9%A1%9E%E3%81%A7false-positive%E3%82%92%E6%8A%91%E3%81%88%E3%82%8B%E6%96%B9%E6%B3%95/shift.png"
width="1536"
height="524"
srcset="https://www.m1ke.org/p/%E5%88%86%E5%B8%83%E5%A4%96%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E5%90%AB%E3%82%80%E5%88%86%E9%A1%9E%E3%81%A7false-positive%E3%82%92%E6%8A%91%E3%81%88%E3%82%8B%E6%96%B9%E6%B3%95/shift_hu_41a43bf2ed4e261b.png 480w, https://www.m1ke.org/p/%E5%88%86%E5%B8%83%E5%A4%96%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E5%90%AB%E3%82%80%E5%88%86%E9%A1%9E%E3%81%A7false-positive%E3%82%92%E6%8A%91%E3%81%88%E3%82%8B%E6%96%B9%E6%B3%95/shift_hu_6f7e4c04a0ed0b87.png 1024w"
loading="lazy"
class="gallery-image"
data-flex-grow="293"
data-flex-basis="703px"
&gt;&lt;/p&gt;
&lt;h3 id="主なデータセットシフト"&gt;主なデータセットシフト&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;シフト&lt;/th&gt;
&lt;th&gt;変化するもの&lt;/th&gt;
&lt;th&gt;不変と仮定するもの&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Covariate Shift&lt;/td&gt;
&lt;td&gt;$P(x)$&lt;/td&gt;
&lt;td&gt;$P(y \mid x)$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Label Shift&lt;/td&gt;
&lt;td&gt;$P(y)$&lt;/td&gt;
&lt;td&gt;$P(x \mid y)$&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Concept Shift&lt;/td&gt;
&lt;td&gt;$P(y \mid x)$&lt;/td&gt;
&lt;td&gt;一般には特に定まらない&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;他にも、次のような表現が使われる。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;用語&lt;/th&gt;
&lt;th&gt;内容&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Domain Shift&lt;/td&gt;
&lt;td&gt;撮影環境、計測方法、データセット、利用環境などのドメインが変化すること&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Sample Selection Bias&lt;/td&gt;
&lt;td&gt;データの選択方法や収集方法によって、観測された分布に偏りが生じること&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;例えば、道路画像が昼から夜に変化するケースはDomain Shiftに当たる&lt;/li&gt;
&lt;li&gt;これによって$P(x)$が変化し、$P(y \mid x)$が変わらないと仮定できる場合はCovariate Shiftとしても扱える&lt;/li&gt;
&lt;li&gt;実際のシステムでは、複数の種類のシフトが同時に発生することもある&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="評価指標"&gt;評価指標&lt;/h2&gt;
&lt;p&gt;False Positiveを議論するときは、precisionとFalse Positive Rateを区別する必要がある。&lt;/p&gt;
$$
\mathrm{Precision} = \frac{TP}{TP + FP}
$$$$
\mathrm{FPR} = \frac{FP}{FP + TN}
$$&lt;p&gt;NOTE:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;precisionは、モデルがpositiveと判定したもののうち、実際にpositiveだった割合を表す&lt;/li&gt;
&lt;li&gt;FPRは、実際にはnegativeだったデータのうち、誤ってpositiveと判定された割合を表す&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;また、すべての入力をnegativeまたは棄却にすれば、FPを0件にすること自体はできる。そのため、安全性を評価する際にはFPだけでなく、recallやcoverageも同時に確認する必要がある。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;recall
&lt;ul&gt;
&lt;li&gt;実際のpositiveをどの程度検出できたか&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;coverage
&lt;ul&gt;
&lt;li&gt;全入力のうち、棄却せず自動判定できた割合&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;selective risk
&lt;ul&gt;
&lt;li&gt;棄却せず受理した予測における誤り率&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="そもそもの前提"&gt;そもそもの前提&lt;/h2&gt;
&lt;h3 id="about-data"&gt;About Data&lt;/h3&gt;
&lt;h4 id="データが全て"&gt;データが全て&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;結局はデータがあるかないかによって全てが決まると言っても過言ではない&lt;/li&gt;
&lt;li&gt;特に評価では実データでやらないとドメインシフトが起きるので正当な評価ができない&lt;/li&gt;
&lt;li&gt;データがあればほとんどの問題は解決するので、めんどくさくても先にデータを集めるべき&lt;/li&gt;
&lt;li&gt;Data is the new oil&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="データ生成が最強"&gt;データ生成が最強&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;もし実際のinferenceデータを生成できるならそれが一番強い&lt;/li&gt;
&lt;li&gt;データの分布の変更や、ハードネガティブにも逐次対応できるから&lt;/li&gt;
&lt;li&gt;ドメインシフトが起きず、データ生成が適用可能な問題を解くのも大切&lt;/li&gt;
&lt;li&gt;例えば、画像分類などで、データ生成による精度向上が良く使われがち&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="gigo"&gt;GIGO&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;garbage in, garbage out&lt;/li&gt;
&lt;li&gt;モデルが弱く特徴が抽出できない事もある&lt;/li&gt;
&lt;li&gt;また最新のモデルにすればいいと考える事もある&lt;/li&gt;
&lt;li&gt;しかし、最新のモデルでも精度の差は数ポイントになることが多い&lt;/li&gt;
&lt;li&gt;すなわちデータがほぼ全てと言っても過言ではない&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="データの特徴を消さない"&gt;データの特徴を消さない&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;音ではメルスペクトル特徴量や画像では圧縮などをやりがち&lt;/li&gt;
&lt;li&gt;推論を高速化する上では重要だが精度を高める上では情報量が全て&lt;/li&gt;
&lt;li&gt;最近ではメルスペクトル特徴量のような人の手によって作られた情報ではなく、wavを直接使う事も多い&lt;/li&gt;
&lt;li&gt;また、図面などのデータでも、情報がスパースなので、圧縮すると消えてしまったりする&lt;/li&gt;
&lt;li&gt;モデルの層に関しても同じでそもそも層に分解可能な情報が残っているのかも重要になる&lt;/li&gt;
&lt;li&gt;もし転移学習などを刷る場合は、学習する前に、サブクラスレベルでDNNのレイヤーの分解能があるかを見極めるべき&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="データの分解"&gt;データの分解&lt;/h4&gt;
&lt;p&gt;次のような基準でデータフォルダは分ける:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;spec/seed（いわゆるデータを生成や収集する前提となるデータの設計書、taxonomyや例など）&lt;/li&gt;
&lt;li&gt;self/others（selfは自分がリアルで作ったもの）&lt;/li&gt;
&lt;li&gt;real/generated&lt;/li&gt;
&lt;li&gt;third_party （他のデータセット用のフォルダ）&lt;/li&gt;
&lt;li&gt;grade（難易度別）&lt;/li&gt;
&lt;li&gt;class（ラベル別）&lt;/li&gt;
&lt;li&gt;sampling_weight（学習時のsampleing weight別）&lt;/li&gt;
&lt;li&gt;positive/negative（正負）&lt;/li&gt;
&lt;li&gt;easy_positive/easy_negative/hard_positive/hard_negative（グレード別正負）&lt;/li&gt;
&lt;li&gt;subclass（特定のサブクラス別、factorized taxonomyとしても有効）&lt;/li&gt;
&lt;li&gt;case（特定のケース別）&lt;/li&gt;
&lt;li&gt;pair（対照学習の為のデータペア）&lt;/li&gt;
&lt;li&gt;test/train/test（holdout用）&lt;/li&gt;
&lt;li&gt;calibration（確信度用）&lt;/li&gt;
&lt;li&gt;generalization dataset （汎化評価用）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;特に、generalization dataset（いわゆる ODD dataset）は用意しないと客観的な性能評価ができない。&lt;/p&gt;
&lt;h4 id="seedが実験の仮説"&gt;seedが実験の仮説&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;seedはデータを収録、生成、収集する上で基本となるデータの設計書&lt;/li&gt;
&lt;li&gt;Data-Centric AIでは、seedがデータ分解能の基本であり最も大切な学習の仮説となるから&lt;/li&gt;
&lt;li&gt;例
&lt;ul&gt;
&lt;li&gt;文字レベルでやる場合は、先にTaxonomy（分類体系）を定義を行い分類する&lt;/li&gt;
&lt;li&gt;もし、音声データでやる場合は、音声学（Phonetics）のIPA（International Phonetic Alphabet）をベースにデータの定義を行う&lt;/li&gt;
&lt;li&gt;難しい課題の場合は、失敗を考慮してgrade（難易度）の定義を行う&lt;/li&gt;
&lt;li&gt;クラスが二値分類ならあえてnegativeを他クラス分類にしてエラーパターン毎に学習させる&lt;/li&gt;
&lt;li&gt;難しいクラスに関してはsubclassを定義してMECEに列挙する&lt;/li&gt;
&lt;li&gt;精度が上がらないサブクラスは、対となるDyadのpair setを作って対象学習をする&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;基本的に、知識空間で論理的にパターンを列挙・網羅して論理的に学習可能なレベルまで落とす&lt;/li&gt;
&lt;li&gt;これが仮説になりデータの分解能になり、人によるデータレベルのinductive biasになる&lt;/li&gt;
&lt;li&gt;実験においてはここが最も大切な仮説になる&lt;/li&gt;
&lt;li&gt;seedはLabel Ontologyと言ったほうが適切かもしれない&lt;/li&gt;
&lt;li&gt;人間が対象世界をどの粒度・軸・関係性で分解するかという「データ仮説」を設計することが大切ということ&lt;/li&gt;
&lt;li&gt;Knowledge Space → Data Spaceへの射影規則だろう&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="実験の前提の前提"&gt;実験の前提の前提&lt;/h3&gt;
&lt;h4 id="安定化モード"&gt;&lt;strong&gt;安定化モード&lt;/strong&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;seedは再現性確保で最も重要な話なので、必ず決定論的に実験ができるようにする&lt;/li&gt;
&lt;li&gt;逆説的に、決定論的にしないと、そこが常に疑えるので、そもそもAIとdiscussionや検証しても意味がなくなる&lt;/li&gt;
&lt;li&gt;処理速度を犠牲にしてでもやるべきであり、最初に安定化モードの試験を数回行うべき&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;set_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;np&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manual_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cuda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;manual_seed_all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;enable_determinism&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cudnn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deterministic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;True&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cudnn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;benchmark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cuda&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;matmul&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;allow_tf32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backends&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cudnn&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;allow_tf32&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;use_deterministic_algorithms&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;warn_only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;strict&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;use_deterministic_algorithms(strict=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;strict&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;, warn_only=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;strict&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s1"&gt;) &amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;+ TF32 無効化 + cudnn.deterministic=True&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 実行&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;set_seed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;seed&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;enable_determinism&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;set_seed / enable_determinism 定義完了&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="seedキャリブレーション"&gt;seedキャリブレーション&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;seedを変えて結果がどのぐらい変わるのかをCheckする&lt;/li&gt;
&lt;li&gt;もしseedを少し変えて結果が大きく変わる場合は、データのとり方などが一番大きなハイパラになる&lt;/li&gt;
&lt;li&gt;基本的にそれだとモデルのアーキの向上による差が減るので、固定もするが、seedに依存しないようにするのも大事&lt;/li&gt;
&lt;li&gt;例えば、ランダムサンプリングで抽出していたのを、固定分割でサンプリングするなど&lt;/li&gt;
&lt;li&gt;ランダムサンプリングだとseedに依存するが、固定分割だったらepochに依存する&lt;/li&gt;
&lt;li&gt;epochは往々にして変えないし、増やしても途中の結果が残るので、途中ので比較すればOK&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="クラス別信頼区間"&gt;クラス別信頼区間&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;最初に同じデータ、同じシードでべき等性テストをするべき&lt;/li&gt;
&lt;li&gt;もし安定化モードが適用できないような確率的な結果になるときは特に重要&lt;/li&gt;
&lt;li&gt;統計的に有意な回数分テストを回して点推定の信頼区間をクラス毎に取るべき&lt;/li&gt;
&lt;li&gt;実験をするなかでクラスやグレードごとに分散は違うのが一因&lt;/li&gt;
&lt;li&gt;これをしないと、いくら変化があっても原因がその変更によるものかはわからなくなる&lt;/li&gt;
&lt;li&gt;これがFirst Stepとなる&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="対処方法"&gt;対処方法&lt;/h2&gt;
&lt;h3 id="特徴抽出機の評価"&gt;特徴抽出機の評価&lt;/h3&gt;
&lt;h4 id="線形probeで特徴抽出できるのかを評価する"&gt;線形Probeで特徴抽出できるのかを評価する&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;最近のAIの開発は強い特徴抽出機と分類Headを分離する事が多い&lt;/li&gt;
&lt;li&gt;その時に、特徴抽出機は外部のものを使うことがあるが、その評価も刷る必要がある&lt;/li&gt;
&lt;li&gt;例えば、Encoderを外部がら使ってEmbeddingするとする&lt;/li&gt;
&lt;li&gt;AとBとCと複数のEncoderを評価する必要があり、かつそもそも特徴が埋め込まれていているのかも考慮刷る必要がある&lt;/li&gt;
&lt;li&gt;例えば、線形プローブでEmbeddingからシンプルな線型回帰ができるか検査する&lt;/li&gt;
&lt;li&gt;色々な層の出力に同じ種類の線形Probeを載せて、どの層で情報が最も読み出しやすいかを比較する&lt;/li&gt;
&lt;li&gt;もしできないなら、そもそも前提の特徴抽出機やその特徴空間にその情報がないことになる&lt;/li&gt;
&lt;li&gt;ただし、仮説の交絡に注意するべき&lt;/li&gt;
&lt;li&gt;つまり、本当の原因ではないものを原因だと勘違いしてしまう事に注意&lt;/li&gt;
&lt;li&gt;一言でいうと、「どの層で、どんな情報が立ち上がって、どこで消えるか」を見る検査&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="対照群付き埋め込み分析"&gt;対照群付き埋め込み分析&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;一言でいうと、データをベクトル化し、注目するグループと比較対象のグループで、埋め込み空間の違いを調べる分析&lt;/li&gt;
&lt;li&gt;例
&lt;ul&gt;
&lt;li&gt;対象群：AIを導入した店舗のレビュー&lt;/li&gt;
&lt;li&gt;対照群：AIを導入していない似た店舗のレビュー&lt;/li&gt;
&lt;li&gt;この2つを比較して、「AI導入後、顧客レビューの内容が変化したか」を調べる&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;つまり、対象群は対照群に比べて、この方向にどれだけ特徴的にずれているかを見極める&lt;/li&gt;
&lt;li&gt;もし、埋め込み後にデータとして特徴抽出の効果が出ているのか否かを調べる&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="データの集め方"&gt;データの集め方&lt;/h3&gt;
&lt;h4 id="posiとnegaの両方を集める"&gt;posiとnegaの両方を集める&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;positiveだけでなく、negativeも意図して集める&lt;/li&gt;
&lt;li&gt;negativeの収集を疎かにすると、実運用でどんな入力が来るか分からないままモデルを評価することになる&lt;/li&gt;
&lt;li&gt;特にhard negativeやOODになりうる入力は、positiveと同じくらい丁寧に設計して集めるべき&lt;/li&gt;
&lt;li&gt;さらなる理由として、positiveがa・b・cという特徴を持っていて本当はaだけを学習させたい場合、b・cは持つがaは持たないnegativeがないと、モデルが間違った特徴を学習してしまう
&lt;ul&gt;
&lt;li&gt;例: 音声からある単語を学習させたかったのに、negativeが不足していたため単語ではなく話者の喋り方を学習してしまった&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="中間データも保存する"&gt;中間データも保存する&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;最終的に使う加工済みデータだけでなく、加工前の中間データも保存しておく&lt;/li&gt;
&lt;li&gt;理由は、後から加工方法自体を見直したくなった時に、収集からやり直さずに済むため&lt;/li&gt;
&lt;li&gt;生の長尺音声やクリップ前の素材など、後段の処理を差し替えられる余地を残しておく&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="ダブルチェックをする"&gt;ダブルチェックをする&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;ラベル付けや収集作業は、1人だけでなく複数人でダブルチェックする&lt;/li&gt;
&lt;li&gt;理由は、収集/ラベリングのミスがそのまま学習データの品質に直結するため&lt;/li&gt;
&lt;li&gt;特にAIやクラウドソーシングで生成/収集したデータは、人の目でのダブルチェックが必須&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="あえて出来合いのデータセットのみでテストを作る"&gt;あえて出来合いのデータセットのみでテストを作る&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;あえて自前データを一切混ぜず、出来合いの公開データセットのみ100%でテストセットを作る&lt;/li&gt;
&lt;li&gt;理由は、生成過程が完全に独立したデータで評価できると、自前データの収集過程に共通する癖への過学習を検知できるため&lt;/li&gt;
&lt;li&gt;精度を追求する本番用ではなく、評価のための物差しとして使う&lt;/li&gt;
&lt;li&gt;特に、他人の作ったデータセットや手法を再現しようとすると、特徴量の計算方法など自分では気づかなかった変えられる点が色々見つかるため、学びや改善幅が大きい&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="データのクラス間の量"&gt;データのクラス間の量&lt;/h3&gt;
&lt;h4 id="class-stratification層化"&gt;Class stratification（層化）&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;元データのクラス比率、つまり経験的な事前確率$P(Y)$をなるべく維持したままtrain/val/testに分割すること&lt;/li&gt;
&lt;li&gt;例えば、元が A:70%, B:20%, C:10%だった場合は、train/val/testもそれぞれ概ね70/20/10とすること&lt;/li&gt;
&lt;li&gt;一般的に、データをsplitする時に、stratified splitして行う&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="class-balancingバランス化"&gt;Class balancing（バランス化）&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;クラス間の影響の偏りを調整する、より広い概念&lt;/li&gt;
&lt;li&gt;データ数そのものを変える場合もあれば、ロスのクラスウェイトのようにloss上の重みだけ変える場合もある&lt;/li&gt;
&lt;li&gt;方法には以下がある
&lt;ul&gt;
&lt;li&gt;oversampling：少数クラスのサンプルを増やす&lt;/li&gt;
&lt;li&gt;undersampling：多数クラスのサンプルを減らす&lt;/li&gt;
&lt;li&gt;サンプリング重み：データ数は変えず、抽選確率を変えてバッチに乗る頻度を調整する&lt;/li&gt;
&lt;li&gt;ロスのクラスウェイト：データ数は変えず、少数クラスのlossへの寄与を大きくする&lt;/li&gt;
&lt;li&gt;Focal Loss：簡単に正解できるサンプルのlossを弱め、難しいサンプルを相対的に重視する&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;また、balancingの一種で、よくやるのが、train splitで各クラスのサンプル数を、以下のABCのように均等にすること&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;元データ
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; A 80 / B 15 / C 5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓ stratified split
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Train Validation Test
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;80 / 15 / 5 80 / 15 / 5 80 / 15 / 5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;必要ならtrainだけbalancingする。
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; ↓
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;33 / 33 / 33
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="oversamplingとundersampling"&gt;oversamplingとundersampling&lt;/h4&gt;
&lt;p&gt;どっちも不均衡データのクラス比率を、学習用データ側で調整する方法。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;oversampling
&lt;ul&gt;
&lt;li&gt;少ない方を水増し&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;undersampling
&lt;ul&gt;
&lt;li&gt;多い方を間引く&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="サンプリング重み"&gt;サンプリング重み&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;oversamplingは、実際には各サンプルに重みをつけて、その重みに比例した確率で抽選する形で実装することが多い&lt;/li&gt;
&lt;li&gt;ロスのクラスウェイトと違い、lossの計算自体は変えず、どのサンプルがバッチに乗るかの確率だけを変える&lt;/li&gt;
&lt;li&gt;インバランスデータの学習に有効&lt;/li&gt;
&lt;li&gt;重みは1/サンプル数（所属グループの件数の逆数）を使う&lt;/li&gt;
&lt;li&gt;loss reweighting（ロスのクラスウェイト）と対になるやり方&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-python" data-lang="python"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;group_counts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;A&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;B&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 各サンプルがどのグループに属するか&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;train_groups&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;A&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;A&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;A&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="s2"&gt;&amp;#34;B&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;B&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ① 各サンプルに「所属グループ件数の逆数」を重みとして付ける&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;sample_weights&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;tensor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="n"&gt;group_counts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;g&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;g&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;train_groups&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;float64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ② その重みに比例して、n_total個のサンプルindexを抽選する&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# replacement=True なので、同じindexが何回選ばれてもOK&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;perm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;multinomial&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;sample_weights&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;n_total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;replacement&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;True&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# ③ 抽選されたindexを使って、実際の学習データを取り出す&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;n_total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;batch_idx&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;perm&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;batch_size&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;x_batch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;train_x&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;batch_idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;y_batch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;train_y&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;batch_idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 普通にlossを計算&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x_batch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;loss&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;criterion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;y_batch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;optimizer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;zero_grad&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;loss&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;backward&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;optimizer&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;step&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="hard-case"&gt;hard case&lt;/h3&gt;
&lt;h4 id="hard-caseの注意"&gt;hard caseの注意&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;特徴の変化のかけ方で有益にも有害にもなるので注意&lt;/li&gt;
&lt;li&gt;例えば、画像系だとFeatureに0でマスクをかけてわざと特徴を消して学習させることがある&lt;/li&gt;
&lt;li&gt;しかし、これを同じように音のデータに適用するとうまくイカないことが多い&lt;/li&gt;
&lt;li&gt;理由は音の場合は不自然さを学習してしまうから&lt;/li&gt;
&lt;li&gt;ゆえに、単調にMaskをかけたりノイズでMaskをかけたり複数の手法を試す必要がある&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="shifted-positive"&gt;shifted positive&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;例えば、「太郎」を取る時に、最低でも３つは作れる&lt;/li&gt;
&lt;li&gt;「〜太郎」、「〜太郎〜」、「太郎〜」みたいな形&lt;/li&gt;
&lt;li&gt;こちらもデータのかさ増しに使える&lt;/li&gt;
&lt;li&gt;この場合は、時系列的なずらしによるpositiveケースを増やす例&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="adversarial-negatives"&gt;adversarial negatives&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;いわゆるハードネガティブの一種&lt;/li&gt;
&lt;li&gt;例えば、「太郎」を取る時に、音的ににているものをnegativeとして用意刷る手法&lt;/li&gt;
&lt;li&gt;「tara」や「soro」みたいな形で一部分を変更した特徴量として作る&lt;/li&gt;
&lt;li&gt;これは意図的に似た音のケースを増やす例&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="truncated-negative"&gt;Truncated Negative&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Trainデータの中でPositiveケースの一部を含むデータをNegativeとして用意する手法&lt;/li&gt;
&lt;li&gt;例えば、「太郎」という名前を分類したい場合は、「〜太」と「郎〜」という２つの前後のTruncated Negativeを用意できる&lt;/li&gt;
&lt;li&gt;全体を学習させたいが、部分で安易に分類させたくない場合に有効&lt;/li&gt;
&lt;li&gt;部分切り出しによってトレインデータも増えるので、データかさ増しにも有効&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="hidden-negative"&gt;Hidden Negative&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;画像などでもFine-grained Classificationなどでも特徴をMaskで隠して学習させる手法が有効とされている&lt;/li&gt;
&lt;li&gt;Hard NegativeをPositiveケースの一部を隠して作る手法&lt;/li&gt;
&lt;li&gt;特徴的なところをベースに学習が進むので、細かな特徴による違いを学習するにはあえて大局的な特徴を隠して、局所特徴を学習させる&lt;/li&gt;
&lt;li&gt;こちらもデータかさ増しにも有効&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="combination"&gt;combination&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;上述のpositive/negaitveのパターンは特徴量の要素によってさらにデータを増やせる&lt;/li&gt;
&lt;li&gt;例えば、「たろう」を取りたく、精度を上げるために、hidden negativeを適用とする&lt;/li&gt;
&lt;li&gt;「たろう」は3文字なので、3要素となり、この要素をABCとすると、正解はABCのみ&lt;/li&gt;
&lt;li&gt;すると、hidden negativeだけでも次のケースが考えられる&lt;/li&gt;
&lt;li&gt;$\binom{3}{2} + \binom{3}{1} + \binom{3}{0}$&lt;/li&gt;
&lt;li&gt;2つ選ぶ：AB、AC、BC（3通り）&lt;/li&gt;
&lt;li&gt;1つ選ぶ：A、B、C（3通り）&lt;/li&gt;
&lt;li&gt;0個選ぶ：選ばない（1通り）&lt;/li&gt;
&lt;li&gt;つまり、順番と要素が必須なので、7パターンのhidden negativeが作れるという事&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="negation-class"&gt;Negation Class&lt;/h3&gt;
&lt;h4 id="noneクラス負例クラスを追加する"&gt;Noneクラス（負例クラス）を追加する&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;低い確信度を単純に棄却するだけでなく、明示的なNoneまたはOtherクラスを追加した&lt;/li&gt;
&lt;li&gt;これにより、モデルは既知のpositiveクラスだけでなく、学習時に与えた負例をNoneとして識別できるようになる&lt;/li&gt;
&lt;li&gt;ただし、Noneクラスを追加しても、あらゆる未知入力を識別できるわけではない&lt;/li&gt;
&lt;li&gt;学習に使用した負例や、それに近いOODには有効でも、まったく異なる未知入力に対して誤って高い確信度を出す可能性は残る&lt;/li&gt;
&lt;li&gt;ただし、Noneは全体負例クラス用のメタクラスなため、実質Noneの1-Recallこそ本来的な全体のFRPになるので注意&lt;/li&gt;
&lt;li&gt;この方法によってNoneのRecall（Noneを見逃さなかった）=&amp;gt;普通のクラスのPrecisionとなる&lt;/li&gt;
&lt;li&gt;つまり、見逃さずに正確に処理できた普通のクラスの評価ができるようになる&lt;/li&gt;
&lt;li&gt;Noneクラスの性能が下がると、当然普通クラスの性能にも影響がでる事になる&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="予め苦手なタスクを加味して対策する"&gt;予め苦手なタスクを加味して対策する&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;例えば、NLPでは否定の表現が実は精度が下がりがちだったりする&lt;/li&gt;
&lt;li&gt;それは特徴がにているにも関わらず、クラス領域外になるから&lt;/li&gt;
&lt;li&gt;そういう既知の弱点に関しては予めデータ・セットのバランスを調整して対処が可能&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="negationの明確化"&gt;Negationの明確化&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;特に、negationがある場合に有効&lt;/li&gt;
&lt;li&gt;例えば、Moveクラスの反対はDon&amp;rsquo;t MoveだがStopクラスもあるとAIは迷う&lt;/li&gt;
&lt;li&gt;その場合は、意味を明確化刷るために、MoveクラスのSlotにNegationを入れてMove(negated=True)のように対応する&lt;/li&gt;
&lt;li&gt;するとメタクラスであるNone（負例クラス）以外は、否定クラスはNegationのスロットで対応ができる&lt;/li&gt;
&lt;li&gt;結果、動かない!=Stopという事を学習しやくくなり精度が上がる&lt;/li&gt;
&lt;li&gt;これはデータセットの無矛盾性とも関連する&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="class-design"&gt;Class Design&lt;/h3&gt;
&lt;h4 id="データセットの原則"&gt;データセットの原則&lt;/h4&gt;
&lt;p&gt;データセットが間違えているとモデルも間違える。&lt;/p&gt;
&lt;p&gt;では、ゲーテではないが、不完全性ではない完璧なデータセットを作れるのか？&lt;/p&gt;
&lt;p&gt;それの暫定的な答えは以下になった:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;完全性
&lt;ul&gt;
&lt;li&gt;不可能、なぜなら入力空間が無限だから（網羅は不可能）&lt;/li&gt;
&lt;li&gt;故に、現実的なユースケースを定義して、目標をおさえるしかない&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;無矛盾
&lt;ul&gt;
&lt;li&gt;可能、なぜならタキソノミーを定義して、運用でカバーができる&lt;/li&gt;
&lt;li&gt;データをAIで作るとしても、細かなレシピがあれば無矛盾を回避可能&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ただし、ソリテスパラドックスのように、境界を定義する問題は曖昧さがあるので注意。&lt;/p&gt;
&lt;h4 id="サブデータセットを作って対処する"&gt;サブデータセットを作って対処する&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;困難は分割せよではないが、サブデータセットを作るのも有効&lt;/li&gt;
&lt;li&gt;サブデータセット = grade（難易度） x クラス数&lt;/li&gt;
&lt;li&gt;そしてそれぞれにtaxonomyとdefinitinoを定義する&lt;/li&gt;
&lt;li&gt;分割統治法のように小さなデータを無矛盾かつ完全に作るのが目的&lt;/li&gt;
&lt;li&gt;大量の矛盾するデータよりも、少量の無矛盾のデータを作るイメージ&lt;/li&gt;
&lt;li&gt;特にクラスの意味がにているinnner-class classificationなどには有効&lt;/li&gt;
&lt;li&gt;結果、graddeでフォルダを切って、クラスでフォルダを切って、そこでさらにtaxonomyで分析してseedを生成するイメージ&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="小さな範囲で学習が可能かのテストをする"&gt;小さな範囲で学習が可能かのテストをする&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;もしデータ・セットが無矛盾かつ完全だった場合は、作成したデータ・セットで学習してgradeの低いものは解けるはずだから&lt;/li&gt;
&lt;li&gt;最初に作ったseedデータをベースにdata generationやaugumentationするまえに、素の状態でテストするのもあり&lt;/li&gt;
&lt;li&gt;小さく試してどのgradeの学習ができるかのCkeckしてから拡張する方がベター&lt;/li&gt;
&lt;li&gt;モデルの容量や学習データが難しいのではなく、学習データが矛盾していたり刷ることがよくあるから&lt;/li&gt;
&lt;li&gt;ただし、小さな多様性のない小さな範囲での実験になるので汎化したまでとは言えない事が多い&lt;/li&gt;
&lt;li&gt;基本をやってから応用を学習するべきという考え方&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="データセットの定義例"&gt;データセットの定義例&lt;/h3&gt;
&lt;h4 id="他クラス分類の例"&gt;他クラス分類の例&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;まずdataについて、種となるデータと生成されたデータは分ける&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data/{seeds, generated}&lt;/code&gt;のような形&lt;/li&gt;
&lt;li&gt;そして、&lt;code&gt;data/seeds/grade1/class_name/{positive,negative}/subclass_name/{def.md, seed.csv}&lt;/code&gt;みたいに切る&lt;/li&gt;
&lt;li&gt;gradeは難易度の事でgrade1~6ぐらいまで定義する&lt;/li&gt;
&lt;li&gt;gradeを定義刷る理由は、うまく行かなかった時にどこまでうまくいくのかを測る目的&lt;/li&gt;
&lt;li&gt;そして、それぞれの$grade \times class \times subclass$について分割統治法のように解いていくイメージ&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; seed1/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; grade1/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; class1/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; positive/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; subclass1/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; subclass2/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; negative/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; subclass1/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; subclass2/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; class2/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; positive/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; subclass1/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; subclass2/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; negative/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; subclass1/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; subclass2/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; grade2/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; class2/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; positive/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; subclass1/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; subclass2/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; negative/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; subclass1/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; subclass2/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h4 id="二値分類の例"&gt;二値分類の例&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;xxx&lt;/code&gt;という名前を呼ぶ二値分類のクラス設計の例&lt;/li&gt;
&lt;li&gt;グレードでトップで分けるのではなく、まず&lt;code&gt;real/synthetic&lt;/code&gt;という&lt;code&gt;source&lt;/code&gt;の軸で分ける&lt;/li&gt;
&lt;li&gt;その後に、grade分け（難易度分け）をあえてクラスに落として、クラス単位でグレードを教える&lt;/li&gt;
&lt;li&gt;この例は単純な一つのクラスの二値分類の例だが、二値以外も入れて5クラス分類にしている&lt;/li&gt;
&lt;li&gt;&lt;code&gt;xxx_voice&lt;/code&gt;フォルダ以外はネガティブケースとなっている&lt;/li&gt;
&lt;li&gt;最後に、サブクラス単位で分割している&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; synthetic/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; xxx_voice/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; easy_simple/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; hard_negative_voice/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; wrong_name_with_prefix/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; arbitrary_prefix/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; bare_other_name/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; real_word_fragment/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; invented_syllable/{nyao, yoon, sonorant_o, consonant_eo, second_mora_eo}/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; no_voice/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; synthetic_noise/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; real/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; xxx_voice/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; easy_simple/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; negative_voice/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; reazon/ vocal_bursts/ st_train_ja/ audioset_ns/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; no_voice/
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; silence_clips/ demand_clips/ esc50/ musan_noise/ noise_only/
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="geometric-class-design"&gt;Geometric Class Design&lt;/h3&gt;
&lt;h4 id="クラスの定義を絞る"&gt;クラスの定義を絞る&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;精度が低いものに対して、データ量を増やして曖昧な境界線を学習させるのではなく、クラス自体の範囲を狭める方法も有効&lt;/li&gt;
&lt;li&gt;クラスの判定対象を狭め、クラス間の境界が太くなるようにモデルの設計をする&lt;/li&gt;
&lt;li&gt;全体の土地を広げて（データを増やして）国境線の太さ（クラスの境界線の太さ）を広げるより、そもそも国土を小さくすれば境界線が太くなるという考え方&lt;/li&gt;
&lt;li&gt;もちろん、モデルの性能が理由の場合もある&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="inner-classでのsubcatのサンプル傾向の割合を考える"&gt;inner-classでのsubcatのサンプル傾向の割合を考える&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;例えば、そのクラスを決定する特徴的なものを属性1とする&lt;/li&gt;
&lt;li&gt;それに対して、次点で優位な特徴を属性2とする&lt;/li&gt;
&lt;li&gt;それらの軸をかけあわせてtaxinomyを作りデータをsubcat（サブカテゴリー）で分類する&lt;/li&gt;
&lt;li&gt;クラス内の特徴を抽出してサンプルを生成するレシピで網羅する作戦&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="inter-classでラベル矛盾がないか"&gt;inter-classでラベル矛盾がないか&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;特に意味の近いクラスを分類する必要があるときは矛盾が発生しやすい&lt;/li&gt;
&lt;li&gt;AというクラスとBというクラスでそれぞれのサンプルの中で一貫した教師信号がないなど&lt;/li&gt;
&lt;li&gt;その場合はクラス間での矛盾がないかのCheckが必要&lt;/li&gt;
&lt;li&gt;そうでもしないと、モデルが安い結論に陥りがち&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="クラスの領域空間をデザインする"&gt;クラスの領域空間をデザインする&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;分類する空間では基本的に2種類あると考えている&lt;/li&gt;
&lt;li&gt;中心となるコアと、その界面であるサーフェス&lt;/li&gt;
&lt;li&gt;例えば、文字ベースの分類だった場合
&lt;ul&gt;
&lt;li&gt;コア
&lt;ul&gt;
&lt;li&gt;意味軸&lt;/li&gt;
&lt;li&gt;人間製でseedで作成&lt;/li&gt;
&lt;li&gt;もしくは実データをサンプリング&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;サーフェス
&lt;ul&gt;
&lt;li&gt;表現軸&lt;/li&gt;
&lt;li&gt;AI製でseedからaugumentation&lt;/li&gt;
&lt;li&gt;例えばパラフレーズの生成など&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;こうすることでクラスの幾何学的な領域を作成することができるようになる&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="クラスの境界用のデータセットを作る"&gt;クラスの境界用のデータセットを作る&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;国土を狭めて国境線を太くしても間違える事はある&lt;/li&gt;
&lt;li&gt;例えば、12クラス分類をしていたとする&lt;/li&gt;
&lt;li&gt;その時に、ある3つのクラスは物理的に意味が近い事がある&lt;/li&gt;
&lt;li&gt;そうすると、そこの境界は間違えやすい&lt;/li&gt;
&lt;li&gt;そのため、クラス際のデータセットが作るのが有効&lt;/li&gt;
&lt;li&gt;シンプルに12個のclassから2つ選ぶ組み合わせは66通り&lt;/li&gt;
&lt;li&gt;そして、境界の際にそれぞれデータが必要なので、66x2=132通りになる（境界の両側にデータを用意する意味で）&lt;/li&gt;
&lt;li&gt;つまり、距離に関わらないのであれば、原理的には132通りの境界クラス用のデータが必要となる&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="クラス境界のデータセットを作るためのクラスの選び方"&gt;クラス境界のデータセットを作るためのクラスの選び方&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;先程の例だと、12クラス分類から2つの組み合わせなので66通り&lt;/li&gt;
&lt;li&gt;ただしそれは大変なので、ターゲットを狙ってやったほうがいい&lt;/li&gt;
&lt;li&gt;例えば、混同行列から66のランキングで誤爆ランキングを出して判断もできる&lt;/li&gt;
&lt;li&gt;他にも何かの指標と相関性をとって判断するもいいかもしれない&lt;/li&gt;
&lt;li&gt;ただし、同じデータを使った相関係数はトートロジーなので注意&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="grade-class"&gt;Grade class&lt;/h3&gt;
&lt;h4 id="評価用のデータをgrade別に用意する"&gt;評価用のデータをgrade別に用意する&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;評価用のデータは難易度別に複数用意するべき&lt;/li&gt;
&lt;li&gt;たとえデータを追加しても基礎能力に悪影響している可能性もある&lt;/li&gt;
&lt;li&gt;そのため、小学校みたいに6段階でgrade別のholdoutのデータセットを用意し評価すると良い&lt;/li&gt;
&lt;li&gt;grade1がいけるからその使い方をするみたいな逃げ道もできるから&lt;/li&gt;
&lt;li&gt;例えば、paraphraseやreason付き、否定も入れるとgrade+1するとか&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;paraphraseの例:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;同意語への置き換え&lt;/li&gt;
&lt;li&gt;受動態と能動態の変換&lt;/li&gt;
&lt;li&gt;品詞の変更&lt;/li&gt;
&lt;li&gt;文の統合・分割&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="データセットの拡張"&gt;データセットの拡張&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;holdout/heldoutだけだと学習ができた事以外の証明にならない&lt;/li&gt;
&lt;li&gt;そのため、difficultケースや全く別に作ってtestデータなどをベースに試験もするべき&lt;/li&gt;
&lt;li&gt;つまり、複数種類のtestやevalのデータ・セットを用意するべき&lt;/li&gt;
&lt;li&gt;また、特に1クラスのevalのサンプルサイズが経験則的には50以下でのholdoutは統計的に優位な数値がでない&lt;/li&gt;
&lt;li&gt;特に、1教科で90点なのと、10教科で90点なのでは意味が全然違うので、分母は加味するべき&lt;/li&gt;
&lt;li&gt;これはedge caseを除いたデータセットを作るなどのスコープの定義にも関わる&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="データの量"&gt;データの量&lt;/h3&gt;
&lt;h4 id="テストデータのサンプル数"&gt;テストデータのサンプル数&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;仮にseedを固定していて実験がべき等に実行でき、２回同じ実験をしたら全く同じ結果が帰ってきてもサンプルサイズは必要&lt;/li&gt;
&lt;li&gt;なぜなら、サンプル数が極端に少ないと、実験の比較では良くても、ドメインシフトが起こっている可能性があるから&lt;/li&gt;
&lt;li&gt;また、テストデータは結局は注ぎ足し継ぎ足しで増やしていくので、結局は数が重要&lt;/li&gt;
&lt;li&gt;また、学習時にはべき等であったとしても、評価時にべき等とは限らないので評価方法にも注意が必要&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="テストデータの相対的な量"&gt;テストデータの相対的な量&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;テストデータの相対的な量も重要&lt;/li&gt;
&lt;li&gt;例えば、AクラスとBクラスがあり、ある実験でBクラスのデータ（ホールドアウトも含めて）を増やすと、Bクラス過多になったしまった&lt;/li&gt;
&lt;li&gt;ここで、Bクラスの精度の良し悪しは判断しやすいが、全体的な精度とAクラスの精度の見極めは難しい&lt;/li&gt;
&lt;li&gt;当然、全体の精度（ミクロ指標）はBクラス過多になっているので、平等な精度の指標ではなくなる&lt;/li&gt;
&lt;li&gt;そして、Aクラスは相対的に量が少なくなり、学習量もこのBクラスに大きく振っている事になる&lt;/li&gt;
&lt;li&gt;つまり、もともと学習していたAクラスの特徴をモデルの容量不足で上書きした可能性もある&lt;/li&gt;
&lt;li&gt;その場合は、モグラたたきではなく、モデル容量の不足が原因ということ（ASRのEncoderがわかりやすい例）&lt;/li&gt;
&lt;li&gt;おうおうにして、データ量も増やしたらモデルのパラメータも一緒に増やす必要がある&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="data-check"&gt;Data Check&lt;/h3&gt;
&lt;h4 id="最小構成でのミニテストoverfit-a-single-batch"&gt;最小構成でのミニテスト（Overfit a single batch）&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;数百万パターンにもなるような大きな予測対象を扱う場合、いきなり全件で学習・評価するだけでなく、そのうち1件だけを使ってデータ生成・学習・テストを一通り試すのも有効&lt;/li&gt;
&lt;li&gt;パイプライン全体（データ生成→学習→推論）を最小構成で一度通してみて、勘所を掴むのが目的&lt;/li&gt;
&lt;li&gt;1件だけの学習すら成功しない（lossが下がりきらない）なら、パターン数を増やす前にやり方自体を疑うべき&lt;/li&gt;
&lt;li&gt;逆に1件で学習できるなら、そのまま件数を横展開していけばいい&lt;/li&gt;
&lt;li&gt;機械学習分野では一般に「overfit a single batch」と呼ばれる、学習パイプラインのsanity checkの定番手法&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="データビュアー"&gt;データビュアー&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;seedデータのデータビュアーは作るべき&lt;/li&gt;
&lt;li&gt;大量データのCheckを高速化するのが目的&lt;/li&gt;
&lt;li&gt;特にデータが多いとデータの品質Checkが大変になるため&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="そもそもデータセットの確認"&gt;そもそもデータセットの確認&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;ゴミを入れたらゴミがでるだけなので、そこのCheckを先にするべき&lt;/li&gt;
&lt;li&gt;traindataが一部間違っていた結果精度が出ていない初歩ミスもある&lt;/li&gt;
&lt;li&gt;それを完全に終わった後に、モデルのパラメーター増やすなどの改善フローに入れる&lt;/li&gt;
&lt;li&gt;そもそも分母のデータは正常なのか、evalのサンプルサイズは足りているのか、エラーの例は妥当かのCheckになる&lt;/li&gt;
&lt;li&gt;特にAIに生成されたデータは確実にCheckが必要&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="前提の制約を変える"&gt;前提の制約を変える&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;可視化後、現実的に精度を高く分類することが厳しいなどがわかるようになることがある&lt;/li&gt;
&lt;li&gt;その場合は、前提の制約を変えて境界線がはっきりでるようにそもそも要件とデータセットを変えるべき&lt;/li&gt;
&lt;li&gt;モデルを大きくでもしない限り難しいこともあるため&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="結果の比較"&gt;結果の比較&lt;/h3&gt;
&lt;h4 id="追加したデータ量の確認"&gt;追加したデータ量の確認&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;基本的に過去の実験と新しくした実験を比較するが、そもそも論データを増やしたら前提が変わる&lt;/li&gt;
&lt;li&gt;例えば、Positive/Negativeの二値分類で、Positiveデータを増やしたとする&lt;/li&gt;
&lt;li&gt;すると、それに引っ張られてモデルのPositiveの出しやすさも変わる&lt;/li&gt;
&lt;li&gt;すなわち、何件そのカテゴリーで追加したのか、それは前回と比べて何％増えたのか、そして、クラス比はどう変わったのかを計算する必要がある&lt;/li&gt;
&lt;li&gt;それらを元にデータ量や比率の違いによる影響のありなしを評価することができる&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="ホールドアウトかの確認"&gt;ホールドアウトかの確認&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;ある実験とある別の実験の比較で追加したデータによる精度の比較をする&lt;/li&gt;
&lt;li&gt;つまり、新しく追加したデータがホールドアウトとして半分とか評価データに使われているなら疑うべき&lt;/li&gt;
&lt;li&gt;特に新しいデータを大量に追加してその結果、評価データが増えて、指標が良くなっただけかもしれないから&lt;/li&gt;
&lt;li&gt;基本的に評価データはホールドアウト以外に評価のみのデータセットを用意しておいてそれでもCheckするべき&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="アブレーション"&gt;アブレーション&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;もし追加したデータやモデルの差を見たいん場合はアブレーションをするべき&lt;/li&gt;
&lt;li&gt;結局は安定化モードで決定論的な結果を出せても、複数の変更を含む実験だと原因が分からなくなる&lt;/li&gt;
&lt;li&gt;機能であれば、XXXを消したバージョン、YYYを消したバージョンなどで追試をすることで何が寄与したのかを出せる&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="サブクラス間の相関"&gt;サブクラス間の相関&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;クラスのpositive/negativeの中のさらにsubclassを定義することで強みと弱みがわかる&lt;/li&gt;
&lt;li&gt;特にデータ量の差よりもそのサブクラスの特性による得意不得意がわかるから&lt;/li&gt;
&lt;li&gt;あえて弱みのサブクラスに対してはOut of Scope対応にしたりそれ専用の対策を入れる事も可能になる&lt;/li&gt;
&lt;li&gt;それができるのも、クラスのさらに細かいSetのサブクラスを定義するから&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="総当りサブクラス"&gt;総当りサブクラス&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;さらに、総当たり系のサブクラスはそもそも論、サブクラスの中の更に詳細なサブサブクラスも把握できる&lt;/li&gt;
&lt;li&gt;例えば、あるクラスのネガティブのあるサブクラスについて、MECEに網羅できるならそれの中のサブサブクラスがモデルの性能を明らかにする&lt;/li&gt;
&lt;li&gt;結局はデータの定義やTaxonomyをしっかりすれば、色々な考察が可能になる&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="exposure"&gt;Exposure&lt;/h3&gt;
&lt;h4 id="サブクラスカバー率とサブクラス学習効率"&gt;サブクラスカバー率とサブクラス学習効率&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;サブクラス毎に量の偏りがある場合は、サブクラスごとの選出率も関係ある&lt;/li&gt;
&lt;li&gt;AとBというサブクラスがあり、Bクラスは量が多いが、AとBを同じ用にサンプリングしていたらカバー率が違う&lt;/li&gt;
&lt;li&gt;仮に新しいBというサブクラスを追加しても精度が上がらない=難しいとはならない点に注意&lt;/li&gt;
&lt;li&gt;また、統計的に仮にカバーを100%していても見ている回数が違う＝学習しやすさが違う場合がある&lt;/li&gt;
&lt;li&gt;その場合は、カバー率だけではなく、学習効率（$sampleされた回数/データセットのサンプル数$）も出すべき&lt;/li&gt;
&lt;li&gt;ステップ数におけるサンプリング数をだして、その上でサブクラスカバー率とサブクラス学習割合を出すべき&lt;/li&gt;
&lt;li&gt;これとサブクラスごとの誤検知率とサブクラス学習割合の相関性をだすと学習が公平だったのかがわかる&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="サブクラスバリエーション効率"&gt;サブクラスバリエーション効率&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;これはData Augumentaionする前提での話&lt;/li&gt;
&lt;li&gt;subset毎にデータaugmentationを変えていた場合は、それもvariationという指標になる&lt;/li&gt;
&lt;li&gt;単純な話サンプル数が多くてもvariationの少ないところからのsamplingか否かで大きく意味が異なる&lt;/li&gt;
&lt;li&gt;そのため、精度との比較はサブクラスの学習効率とともに、variation数も比較する必要がある&lt;/li&gt;
&lt;li&gt;バリエーション効率は$variation/samples$で求める&lt;/li&gt;
&lt;li&gt;一般的に、量が多いデータセット程複雑なパターンがあるという前提で効率を求める&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="露出と表現の問題"&gt;露出と表現の問題&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;仮にあるサブクラスの精度が低く、サブクラスのvariationとカバー率を上げたとする&lt;/li&gt;
&lt;li&gt;その結果、サブクラスは相対的、絶対的に増えたにも関わらず精度が上がらなかったとする&lt;/li&gt;
&lt;li&gt;この場合は、データの露出は増やしたのに、表現が上がらなかった事を意味する&lt;/li&gt;
&lt;li&gt;すなわち、Exposureの問題ではなくExpressionの問題と判断する事ができる&lt;/li&gt;
&lt;li&gt;Expressionとは、例えば分類問題などであった場合はHeadの話&lt;/li&gt;
&lt;li&gt;特に、すでに線形Probeなどでモデルの層に線形分類するだけの特徴がある場合などは強くその仮説が支持される&lt;/li&gt;
&lt;li&gt;例えば、分類層では、mean poolingをしていたがそれが特徴をけしていた、など&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="error-analysis"&gt;error analysis&lt;/h3&gt;
&lt;h4 id="データリーケージに注意する"&gt;データリーケージに注意する&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;データリーケージ（Data Leakage）とは、本来モデルが知り得ない情報が、学習や評価のプロセスに紛れ込んでしまうこと&lt;/li&gt;
&lt;li&gt;エラーアナリシスにおいては、testデータを見て傾向をつかみ、その結果を元にモデルやデータを改善するというサイクルを繰り返すこと自体が、一種のデータリーケージになる&lt;/li&gt;
&lt;li&gt;繰り返すうちにtestデータが実質的なvalidationデータ化してしまい、最終的な評価値が楽観的に偏る&lt;/li&gt;
&lt;li&gt;これは後述のWinner&amp;rsquo;s Curseと同じ、同じデータで選択と評価を繰り返すことで生じるバイアスの一種&lt;/li&gt;
&lt;li&gt;そのため、エラーアナリシスは基本的にvalidationデータで行い、testデータは最終確認のためだけに温存するべき&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="winners-curse勝者の呪いに注意"&gt;Winner&amp;rsquo;s Curse（勝者の呪い）に注意&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;複数のモデル、checkpoint、しきい値、ハイパーパラメータを同じ検証データで比較し、その中で一番良かったものを選ぶと、選ばれた候補の評価値は本来の実力より楽観的に偏る&lt;/li&gt;
&lt;li&gt;理由は、比較対象の数が増えるほど、たまたま検証データとの相性が良かっただけの候補が選ばれやすくなるため&lt;/li&gt;
&lt;li&gt;元はオークション理論の用語で、複数の入札者が同じ対象の価値を誤差込みで見積もったとき、最も高く見積もった入札者（勝者）の見積もりは実際の価値より高くなりがちという現象&lt;/li&gt;
&lt;li&gt;意思決定論では同じ現象をoptimizer&amp;rsquo;s curseとも呼ぶ&lt;/li&gt;
&lt;li&gt;データリーケージが「testデータを見て判断や改善を繰り返すこと」自体の問題なのに対し、Winner&amp;rsquo;s Curseは「パラメータや候補を最適化（比較・選択）した結果、選ばれたものの評価値が偏る」という、選択操作そのものに起因するバイアス&lt;/li&gt;
&lt;li&gt;対処方法
&lt;ul&gt;
&lt;li&gt;選定に使ったのとは別のホールドアウトデータで、選ばれた候補を再評価する&lt;/li&gt;
&lt;li&gt;比較する候補の数を絞る、または候補間の差が誤差の範囲内かを検定する&lt;/li&gt;
&lt;li&gt;checkpointの学習時evalを鵜呑みにしないという注意点（後述）も、この現象の一種&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;NOTE:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;いわゆる「データ最適化バイアス」（同じデータに対して選択と評価を繰り返すことで生じる楽観バイアス）と、この現象は本質的に同じもの&lt;/li&gt;
&lt;li&gt;しきい値選定、モデル選定、特徴量選定、ハイパーパラメータ探索など、何であれ「同じデータで何度も比較して一番良いものを選ぶ」という操作が入る限り発生する&lt;/li&gt;
&lt;li&gt;呼び方が違うだけで、根っこにあるのは「選択に使ったデータで、選んだものをそのまま評価してはいけない」という同一の問題&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="エラーアナリシス"&gt;エラーアナリシス&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;言わずもがなエラーアナリシスを行うのが一番大切&lt;/li&gt;
&lt;li&gt;どこがエラーになり、なぜエラーになったのかの分析のこと&lt;/li&gt;
&lt;li&gt;特にエラーの分類（Subcategorization）を行い、何が弱いのかを具体的に判断する&lt;/li&gt;
&lt;li&gt;他には、一番重要なのはNoneクラスのRecall（全体見逃し）やその他のクラスのPrecisionなど&lt;/li&gt;
&lt;li&gt;subcat毎に指標を置いて、エラーの傾向や割合をCheckする&lt;/li&gt;
&lt;li&gt;別の言い方だと、Don&amp;rsquo;t boil the oceanという考え方に近い&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="データを可視化する"&gt;データを可視化する&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;例えば、PCA、T-SNE、UMAPなどで次元を削減して可視化するのも一つの手&lt;/li&gt;
&lt;li&gt;クラスタリングしてクラスごとの境界線を可視化するとどのぐらい分けれているのかがわかるため&lt;/li&gt;
&lt;li&gt;そこで実際にコードを動かして結果を元にプロットするとどのクラスターに近いかがわかる&lt;/li&gt;
&lt;li&gt;実際に手で動かすと、実はWhack-a-Moleパターンだとわかることもある&lt;/li&gt;
&lt;li&gt;単に混同行列だけではなく、推論用にクラス境界マップを作るがおすすめ&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="確信度の高いfalseの境界ペアの可視化"&gt;確信度の高いfalseの境界ペアの可視化&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;確信度の高いfalseの境界ペアを明らかにするのが一番当たり前だが最も重要&lt;/li&gt;
&lt;li&gt;確信度が90%以上で間違った例を確認して、どのクラスがどのクラスと間違えたのかという情報&lt;/li&gt;
&lt;li&gt;例えば、AをBと間違えたなら、その分類の境界線が怪しいということ&lt;/li&gt;
&lt;li&gt;それらのfalseの境界ペアに対して、境界線をはっきりさせる為のサンプルを追加すればいいという事&lt;/li&gt;
&lt;li&gt;そして、Noneが全体を網羅する負例クラスとして存在するため、幾何学的にNoneはあらゆるクラスと接する&lt;/li&gt;
&lt;li&gt;そのため、Noneに関してはepoch毎に入れ替えて多様性を加味して多角的に接点を学習させる必要がある&lt;/li&gt;
&lt;li&gt;普通のクラス境界よりもNoneのクラス境界は多次元的に接していると考えられるため&lt;/li&gt;
&lt;li&gt;別の言い方をすると、あるAクラスは狭いので他のクラスと距離がある、しかし、Noneは広いので他のクラスと距離が近いということ&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="信頼度をキャリブレーション"&gt;信頼度をキャリブレーション&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;分類器が出力する信頼度 (confidence) が、分類結果が真に正しい可能性を表すように補正する&lt;/li&gt;
&lt;li&gt;これを、信頼度キャリブレーション (confidence calibration) と呼ぶ&lt;/li&gt;
&lt;li&gt;つまり、AIや自動化システムへの信頼を、そのシステムの実際の当たりやすさに近づけていく手法&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="エラーのクラス分類"&gt;エラーのクラス分類&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;特にしきい値が高いときのエラーの内訳が大切&lt;/li&gt;
&lt;li&gt;その上で、genuine vs. quetionableなのかが最初のフィルターになる&lt;/li&gt;
&lt;li&gt;つまり、評価データで正しくGTのラベルがついいているかいないかという事&lt;/li&gt;
&lt;li&gt;さらに、それがグレード別にどうなのかの判断も大切&lt;/li&gt;
&lt;li&gt;ODDだった場合はグレード外やtaxonomyのヌケモレだったということ&lt;/li&gt;
&lt;li&gt;エラー内のクラス分類をして間違えたOODに対して、対策するのが正しい&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="nearest-neighbor-analysis"&gt;Nearest Neighbor Analysis&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;学習して分析し、最もエラーが高かったものの一覧を出す&lt;/li&gt;
&lt;li&gt;それをベースに、まずはどんな特徴があるのかをCheckする&lt;/li&gt;
&lt;li&gt;さらに、そのエラーの特徴量一つ一つの特徴量と一番近い学習データの特徴量を出す&lt;/li&gt;
&lt;li&gt;これによって、そのエラーが何のトレインによって学習されたのかがわかる&lt;/li&gt;
&lt;li&gt;例えば、ある似たTrainの集合A, B, Cというデータがあり、それぞれ特徴量DとEを共有する&lt;/li&gt;
&lt;li&gt;しかし、実際にDを学習させる目的で学習したらEも学習してしまったみたいな事もある&lt;/li&gt;
&lt;li&gt;その場合は、Eを含むNear Negativeなデータを含めればいいとわかる&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="評価"&gt;評価&lt;/h3&gt;
&lt;h4 id="かならず複数しきい値で評価する"&gt;かならず複数しきい値で評価する&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;しきい値を一つで評価するとモデルの評価はできない&lt;/li&gt;
&lt;li&gt;AUCも複数種類でやるべきであり、かつclassやsubclassレベルでもやるべき&lt;/li&gt;
&lt;li&gt;特に、モデルのアーキを良くしたのに精度を一つの指標で評価するのは危険&lt;/li&gt;
&lt;li&gt;性能が上がっているのに、見落としている事が往々にしてあるから&lt;/li&gt;
&lt;li&gt;最低でもROC-AUCとPR-ACU、pAUC（この場合は、FPRのpartial AUC=部分AUCなど）で評価するべき&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="不均衡データではauc系より実運用の分母に寄り添った指標を使う"&gt;不均衡データではAUC系より実運用の分母に寄り添った指標を使う&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;極端に不均衡なデータでは、AUC系の指標だけでは実運用の感覚とズレることがある&lt;/li&gt;
&lt;li&gt;理由は、AUCは正例・負例のペアの順位関係を見る指標なので、負例が膨大にあっても比較的高い値が出やすいため&lt;/li&gt;
&lt;li&gt;実運用の分母（1時間あたり・1日あたりの発話数など）に寄り添った指標を使うべき
&lt;ul&gt;
&lt;li&gt;例: FA/hour（1時間あたりの誤発火数）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;こうすることで、「実際に運用したらどれくらいの頻度で誤動作するか」が直感的に分かる&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="マクロ指標とマイクロ指標でみる"&gt;マクロ指標とマイクロ指標でみる&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;全体としては性能が下がっていように見えても部分では性能が上がっている可能性がある&lt;/li&gt;
&lt;li&gt;全サンプルを均等に評価するマイクロ指標サブクラスやクラス毎に出して評価するマクロ指標は分けるべき&lt;/li&gt;
&lt;li&gt;特にある一部の問題を解決できるが全体の性能が下がるトレードオフがあるのが普通&lt;/li&gt;
&lt;li&gt;全体評価と個別評価は分けて述べるべき&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="アップストリーム指標の注意"&gt;アップストリーム指標の注意&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;前提となるepoch選定には学習時のアップストリーム指標のみで測るとbestモデルを選べないことがある&lt;/li&gt;
&lt;li&gt;故に、analysisの前提となるepochについては、ダウンストリーム指標で測るのがベター&lt;/li&gt;
&lt;li&gt;すなわち、eval_lossとかだけではなく、この例で言うと、FPRやNone-Recall、クラスのPrecisionなどで総合的にクラスやサブクラスベースで測るべき&lt;/li&gt;
&lt;li&gt;安易にeval_lossでbestを出さないべき&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="checkpointの学習時evalを鵜呑みにしない"&gt;checkpointの学習時evalを鵜呑みにしない&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;学習中に自動で出るcheckpointごとのeval結果は、軽量・簡易な指標であることが多い&lt;/li&gt;
&lt;li&gt;良さそうなcheckpointが見つかっても、その数値をそのまま信じてモデルを選定しない&lt;/li&gt;
&lt;li&gt;後で改めて、ちゃんとした評価指標・評価データでそのcheckpointを洗い直すべき&lt;/li&gt;
&lt;li&gt;学習時evalと本番想定の評価は、計算方法やデータの粒度が異なることが多く、乖離が起きうるため&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="winners-curseパラメータ最適化のバイアス"&gt;Winner&amp;rsquo;s Curse（パラメータ最適化のバイアス）&lt;/h4&gt;
&lt;p&gt;Winner&amp;rsquo;s Curseについては、前述の「error analysis」内の同名セクションを参照。&lt;/p&gt;
&lt;h4 id="足し算じゃない相互作用に注意"&gt;足し算じゃない相互作用に注意&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;変更一つに対して精度が下がってもそれが必ずしも悪影響だったかというとそうでもないこともあるので注意&lt;/li&gt;
&lt;li&gt;例えば、以下みたいなことがある
&lt;ul&gt;
&lt;li&gt;Aという変更=&amp;gt;精度が下がった&lt;/li&gt;
&lt;li&gt;Bという変更=&amp;gt;精度が下がった&lt;/li&gt;
&lt;li&gt;しかしAとBを両方同時に入れた=&amp;gt;精度が上がった&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;故に、一般的に論理的に考えて有効な手法でも前提条件が揃わず下がる事があるので注意&lt;/li&gt;
&lt;li&gt;自分の例では、multi-head attention pool x exposure設定の向上 などであった&lt;/li&gt;
&lt;li&gt;かっこよくいうと、non-additive interactionというらしい（マイナスかけてプラスだから？）&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="ood対策の評価方法"&gt;OOD対策の評価方法&lt;/h4&gt;
&lt;p&gt;OOD対策は、単一のテストデータだけで評価しないほうがよい。&lt;/p&gt;
&lt;p&gt;例えば、次のように評価データを分ける。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;IDのpositive&lt;/li&gt;
&lt;li&gt;IDのnegative&lt;/li&gt;
&lt;li&gt;IDに近いnear-OOD&lt;/li&gt;
&lt;li&gt;IDから明確に離れたfar-OOD&lt;/li&gt;
&lt;li&gt;実際の運用ログから作ったデータ&lt;/li&gt;
&lt;li&gt;時期、ユーザー、環境などで分けたデータ&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;確認する指標には次のようなものがある。&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;対象&lt;/th&gt;
&lt;th&gt;主な指標&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;通常の分類&lt;/td&gt;
&lt;td&gt;Precision、Recall、FPR、F1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OOD検出&lt;/td&gt;
&lt;td&gt;AUROC、AUPR、FPR@95TPR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;棄却を含む分類&lt;/td&gt;
&lt;td&gt;Coverage、Selective Risk&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;安全性&lt;/td&gt;
&lt;td&gt;受理した予測におけるFP件数・FP率と信頼区間&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;しきい値は検証データで決定し、最終評価データを確認してから変更しないことも重要である。&lt;/p&gt;
&lt;p&gt;評価データ上でFPが0件だった場合も、「観測されたFPが0件だった」と表現し、将来のFPが0であると断定しない。&lt;/p&gt;
&lt;h3 id="model"&gt;model&lt;/h3&gt;
&lt;h4 id="学習率の選択"&gt;学習率の選択&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;いろいろな手法を試していて、早いepochでlossが下がりすぎる傾向がある場合は、学習率が高すぎることを疑うべき&lt;/li&gt;
&lt;li&gt;学習率の調整だけでも、スコアが良くなることが多い&lt;/li&gt;
&lt;li&gt;迷ったら、その手法のベースになっている論文で使われている学習率を採用するのが安牌&lt;/li&gt;
&lt;li&gt;桁を変えた複数の学習率（例: 1e-3、1e-4、1e-5）で実際にチェックして比較するべき&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="入力データを増やす"&gt;入力データを増やす&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;基本的に入力に対してモデルは学習する&lt;/li&gt;
&lt;li&gt;もしどんなに頑張っても精度が上がらないときは、シンプルに入力データを増やすといい&lt;/li&gt;
&lt;li&gt;あるEncoderをFreezeしたモデルで使ったクラス分類の時に、そのEncoderの中間データを使った&lt;/li&gt;
&lt;li&gt;一般的にEncoderの最後の出力を使うと、既に丸められたデータだったから&lt;/li&gt;
&lt;li&gt;このように、入力が抽象化されて弱いときはあえて、低次元のプリミティブな情報を取ってモデルを構築するのもあり&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="マルチエンコーダーを組み合わせる時の注意"&gt;マルチエンコーダーを組み合わせる時の注意&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;複数のEncoderの出力をシンプルにconcatして学習に使ったが、精度が上がらなかった&lt;/li&gt;
&lt;li&gt;原因は、Encoderごとに出力のスケールが違うものをそのままconcatしていたこと&lt;/li&gt;
&lt;li&gt;スケールの大きいEncoder側の情報にモデルが引っ張られてしまい、スケールの小さい方の情報が実質的に無視されてしまう&lt;/li&gt;
&lt;li&gt;対処方法はz値化&lt;/li&gt;
&lt;li&gt;各Encoderの出力を平均0・分散1に標準化してからconcatすることで、スケールの違いによる偏りをなくす&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="モデルを厚くする"&gt;モデルを厚くする&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;モデルのパラメータと学習量には相関性がある&lt;/li&gt;
&lt;li&gt;ASRなどではEncoderの学習できる量はそのパラメーター数で決まる&lt;/li&gt;
&lt;li&gt;例えば、大きな学習データを小さいEncoderで学習しようとしても忘却してしまう&lt;/li&gt;
&lt;li&gt;もしくは、小さな学習データを大きなEncoderで学習しても過学習するだけ&lt;/li&gt;
&lt;li&gt;モデルの容量とトレインデータのバランスが取れるモデルの厚みにするべき&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="残差とconcat"&gt;残差とconcat&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;ResNetのような加算による残差を伝えたり、concatして長いベクトルにする方法もある&lt;/li&gt;
&lt;li&gt;例えば、Conv層で情報を近傍の特徴を抽出しているが、それが原因だと判断した場合&lt;/li&gt;
&lt;li&gt;その場合は、そのConv層で行う入力を残差ブロックやConcatで繋げて後ろに伝える方法&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="pe"&gt;PE&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;TransformerではPositional Encodingを行う&lt;/li&gt;
&lt;li&gt;それを応用してabsolute/relativeなPEを使って位置情報を明示的に特徴にする方法もある&lt;/li&gt;
&lt;li&gt;Conv層の場合は、局所的な特徴をFilterするのでbiasとして織り込まれているが、明示的にPEをやるのもあり&lt;/li&gt;
&lt;li&gt;ただし、絶対的なPEを使うと、その場所にその情報があることを過学習してしまう可能性もあるので注意&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="poolingをattentionに"&gt;PoolingをAttentionに&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;poolingをAttentionするのも有効な手法&lt;/li&gt;
&lt;li&gt;局所特徴をConvなどで抽出したとに、情報を抽象化する意味でPool層を入れる事はあるが、情報がぼやけてしまうことがある&lt;/li&gt;
&lt;li&gt;例えば、mean poolなどでは、結局はpoolはそれぞれを重みをなく要約するので重要な情報が落ちる可能性がある&lt;/li&gt;
&lt;li&gt;この場合は平均に要約されて、最大や最小の情報が消えてしまっている&lt;/li&gt;
&lt;li&gt;そのため、Query attention poolingやmulti head attention poolingなどでどこのpoolを重要視するかを学習させるのもあり&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="convをattentionに"&gt;ConvをAttentionに&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;ConvをAttention系にするのも有効な手法&lt;/li&gt;
&lt;li&gt;Convだと近傍の特徴をまとめるinductive biasがかかっているが、全体をみたい時などにはself-attentionなどが有効&lt;/li&gt;
&lt;li&gt;特に、multi-head self-attentionで複数のhead（見方）から高度なattentionをかけて特徴抽出を学習することができる&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="不均衡データの学習方法"&gt;不均衡データの学習方法&lt;/h3&gt;
&lt;h4 id="none負例をepochごとに入れ替える"&gt;None（負例）をepochごとに入れ替える&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Noneクラスとして利用できる負例が大量にある場合、各epochで異なる負例をサンプリングした&lt;/li&gt;
&lt;li&gt;これにより、Noneクラスの件数を極端に増やさずに、多様な負例をモデルへ提示できる&lt;/li&gt;
&lt;li&gt;ただし、epochごとにランダムに入れ替えるだけではhard negative miningとは限らない&lt;/li&gt;
&lt;li&gt;現在のモデルが高いpositiveスコアを出した負例を優先的に選ぶ場合に、hard negative miningと呼べる&lt;/li&gt;
&lt;li&gt;また、学習時のクラス比率を変えること自体は禁止ではない&lt;/li&gt;
&lt;li&gt;クラス比率、サンプリング方法、クラス重みは、precision、recall、確率校正に影響するため、目的に合わせて検証する必要がある&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="損失関数の重みで調整"&gt;損失関数の重みで調整&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;epoch毎に入れ替えて調整する方法もあるが、損失関数側で重みで調整する方法もある&lt;/li&gt;
&lt;li&gt;例えば、Positiveケースが少なく、Negativeケースが多い場合、あえてそのままで学習する&lt;/li&gt;
&lt;li&gt;しかし、損失関数側でPositiveの重みを$\frac{neg}{pos}$倍に調整して、Positive側を大きく調整することとする&lt;/li&gt;
&lt;li&gt;これによって、Positiveを学習しやすく、Negativeを学習しにくくし、影響をコントロールできる&lt;/li&gt;
&lt;li&gt;少量のソースのサブクラスは普通にepochで全部学習さ、大量のソースのものはsamplingする形になる&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="ロスのクラスウェイト"&gt;ロスのクラスウェイト&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;ロスのクラスウェイトは「そのサンプルが何クラスか」で重みを決る&lt;/li&gt;
&lt;li&gt;たとえば A:1%, B:99%なら、Aクラスはweight = 99、Bクラスはweight = 1&lt;/li&gt;
&lt;li&gt;つまり、Aはサンプルが少ないので、Aのサンプルなら簡単なAでも難しいAでも全部重くするイメージ&lt;/li&gt;
&lt;li&gt;データ数は変えず、少数クラス1件あたりのlossへの寄与を大きくする&lt;/li&gt;
&lt;li&gt;ロスのクラスウェイトにはサンプル数の逆数が使える
&lt;ul&gt;
&lt;li&gt;lossの計算時にクラスごとの重みを掛けて調整する&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="focal-loss"&gt;Focal Loss&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;クラス数そのものではなく、予測の難しさに応じて各サンプルのlossを調整する&lt;/li&gt;
&lt;li&gt;ロスのクラスウェイトと違い、Focal Lossは「そのサンプルをモデルがどれくらい簡単に当てられているか」で重みを変える&lt;/li&gt;
&lt;li&gt;例
&lt;ul&gt;
&lt;li&gt;正解を99%確信している → ほぼ無視&lt;/li&gt;
&lt;li&gt;正解を60%くらい → そこそこ重視&lt;/li&gt;
&lt;li&gt;正解を10%しか出せない → 強く重視&lt;/li&gt;
&lt;li&gt;つまり、クラスではなく難易度を見る&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;少数クラスは学習が浅い段階では確信度が低い（＝難しい）ことが多いため、Focal Lossはロスレベルで結果的にマイノリティクラスへ重みをかけることになる
&lt;ul&gt;
&lt;li&gt;そのため、Focal Lossを使う場合はロスのクラスウェイトを併用してはいけない（重みが二重に乗ってしまうため）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="ロスのクラスウェイトとfocal-lossの比較"&gt;ロスのクラスウェイトとFocal Lossの比較&lt;/h4&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;-&lt;/th&gt;
&lt;th&gt;ロスのクラスウェイト&lt;/th&gt;
&lt;th&gt;Focal Loss&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;重みの基準&lt;/td&gt;
&lt;td&gt;クラス&lt;/td&gt;
&lt;td&gt;サンプルの難しさ&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;少数クラス&lt;/td&gt;
&lt;td&gt;直接重くする&lt;/td&gt;
&lt;td&gt;直接は関係ない&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;簡単なサンプル&lt;/td&gt;
&lt;td&gt;少数クラスなら重いまま&lt;/td&gt;
&lt;td&gt;軽くする&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;主な目的&lt;/td&gt;
&lt;td&gt;クラス不均衡対策&lt;/td&gt;
&lt;td&gt;Easy sample が大量にある問題への対策&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;まとめると、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ロスのクラスウェイト：「少数クラスだから重要視」&lt;/li&gt;
&lt;li&gt;Focal Loss：「まだ上手く分類できないから重要視」&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;注意点として、不均衡データに対して既にロスのクラスウェイトで対応している場合、そこにFocal Lossを追加すると重みが二重に乗ってしまい逆効果になる。Focal Lossを使うなら、ロスのクラスウェイトは外す必要がある。&lt;/p&gt;
&lt;h3 id="不揃いデータの学習方法"&gt;不揃いデータの学習方法&lt;/h3&gt;
&lt;p&gt;クラスごとの数が偏っている不均衡データとは別に、サンプルごとの難易度や質にばらつきがある不揃いなデータもある。この場合、いきなり全データを均等に学習させるより、まず粗く全体像を学習してから徐々に詳細や難しいケースに踏み込んでいく方が有効なことがある。&lt;/p&gt;
&lt;h4 id="カリキュラム学習"&gt;カリキュラム学習&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;人間が易しい問題から難しい問題へ順に学ぶのと同じように、モデルにも易しいサンプルから難しいサンプルへ順に学習させる手法&lt;/li&gt;
&lt;li&gt;いきなり難しい・ノイズの多いサンプルを学習させると、学習が不安定になったり収束が遅くなったりすることがある&lt;/li&gt;
&lt;li&gt;「易しい」の基準はタスクによって様々で、ラベルの確信度が高い、サンプルサイズが小さい、ノイズが少ない、などが使われる&lt;/li&gt;
&lt;li&gt;学習が進むにつれて、徐々に難しい・不揃いなサンプルの比率を上げていく&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="破滅的忘却catastrophic-forgetting"&gt;破滅的忘却（Catastrophic Forgetting）&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;段階的に学習を進める際、新しい（後段の）データばかり学習させると、以前学習した内容を忘れてしまう現象&lt;/li&gt;
&lt;li&gt;カリキュラム学習のように学習フェーズを分けたり、新しいドメインのデータを後から追加学習させたりする場合に起きやすい&lt;/li&gt;
&lt;li&gt;対策
&lt;ul&gt;
&lt;li&gt;新しいデータだけでなく、古いデータも一定割合混ぜて学習を続ける（リハーサル）&lt;/li&gt;
&lt;li&gt;学習率を下げて、既存の重みを大きく壊さないようにする&lt;/li&gt;
&lt;li&gt;重要なパラメータの変化を抑制する正則化を加える&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="train"&gt;train&lt;/h3&gt;
&lt;h4 id="データを増量する時の注意"&gt;データを増量する時の注意&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;例えば、全体のデータ量を数倍にするのは一般的にやる方法&lt;/li&gt;
&lt;li&gt;ただし、Train中にあるカテゴリーが弱いからとそこだけ増やすと問題が発生する事が多い&lt;/li&gt;
&lt;li&gt;発生するのは 「1カテゴリだけ拡張すると他が犠牲になる」問題&lt;/li&gt;
&lt;li&gt;ある少量のサンプルを過度に拡張すると、過学習が起きる&lt;/li&gt;
&lt;li&gt;つまり、データAugmentationなどをする場合は、もともとの多様性が重要ということ&lt;/li&gt;
&lt;li&gt;言い換えると
&lt;ul&gt;
&lt;li&gt;もともとのユニークなものが多い（多様性がある）=&amp;gt;増やしても偏らない=&amp;gt;全体的な底上げになる&lt;/li&gt;
&lt;li&gt;もともとのユニークなものが少ない=&amp;gt;増やすと偏り過学習になる=&amp;gt;り他のクラスも低下させる、もぐらたたきになる。&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;言い換えると、データ量とモデル容量の関係はもちろん容量の問題もあるが、データ量の多様性の問題もある&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="実験ログとdiscussionログを残す"&gt;実験ログとdiscussionログを残す&lt;/h4&gt;
&lt;p&gt;実験に再現性と論理性、実論性、問題の真性を検証すためにいくつかの工夫が必要。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;フォルダ分けする
&lt;ul&gt;
&lt;li&gt;再現性のためexperimentsフォルダを切ってそこで作業する&lt;/li&gt;
&lt;li&gt;例えば、&lt;code&gt;experiments/01_xxx&lt;/code&gt;みたいな形&lt;/li&gt;
&lt;li&gt;そこの中で完結するようにして、独立性を高める（例えば、過去の実験コードなどは使わない）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;実行logなどは必ず全て保存する
&lt;ul&gt;
&lt;li&gt;データドリブンでAIに開発させるのが目的&lt;/li&gt;
&lt;li&gt;データがあれば問題の再現性を検証できる&lt;/li&gt;
&lt;li&gt;AIにGuessさせない&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;mdベースでissueについてdiscussionする
&lt;ul&gt;
&lt;li&gt;理由は、課題が再現しても（原因がわかっても）解決するべき本物の課題かを議論する&lt;/li&gt;
&lt;li&gt;前提が間違っていたり、インパクトが小さかったり、実現可能性が引くい可能性もある&lt;/li&gt;
&lt;li&gt;issueの真因究明と本物の課題かどうかをまずは見極める&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;mdベースでsolutionのdiscussionをする
&lt;ul&gt;
&lt;li&gt;解決策についてもmdでdiscussionする&lt;/li&gt;
&lt;li&gt;特に課題の原因と価値が明らかになったら次は打ち手の検証が必要だから&lt;/li&gt;
&lt;li&gt;打ち手についてもA vs Bのよう形で検証して結論を出してからsrcのコードに反映する&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="lossのトレンドの見方"&gt;lossのトレンドの見方&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;train lossとvalid lossがどちらもまだ下がっている場合は、epoch数を増やした方がいい
&lt;ul&gt;
&lt;li&gt;収束しきっていないのに学習を打ち切ると、伸びしろを捨てることになる&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;wandbなどでlossを可視化して管理するべき
&lt;ul&gt;
&lt;li&gt;数値の羅列だけでは傾向がつかみにくく、グラフで見た方がトレンドが分かりやすい&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;プラトー（lossがほぼ横ばいになっている状態）にいるかどうかを確認する
&lt;ul&gt;
&lt;li&gt;プラトーに入っているなら、そのままepochを増やしても伸びない可能性が高い&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="val_lossの設計"&gt;val_lossの設計&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;train lossだけを見ていると過学習に気づけないので、val lossが大切&lt;/li&gt;
&lt;li&gt;val_lossの計算をやりすぎると重くなるので、毎epochではなく数epochに一度でもいい&lt;/li&gt;
&lt;li&gt;最終的にはtestで使うような指標でval_lossを評価するべき
&lt;ul&gt;
&lt;li&gt;valとtestで見ている指標がズレていると、valが良くてもtestで期待通りの結果にならないことがある&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="dropout"&gt;Dropout&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;trainで過学習を抑える目的で使う&lt;/li&gt;
&lt;li&gt;非決定性の原因になりうるが、精度向上のためには入れるべき&lt;/li&gt;
&lt;li&gt;スパイスのようなもの——少し入れるだけで効いてくる&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="focal-loss-1"&gt;Focal Loss&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;BCEだと、簡単な問題も難しい問題も同じように学習してしまう&lt;/li&gt;
&lt;li&gt;Focal Lossを使うと、難しい問題（誤分類しやすい/確信度が低い例）により大きなウェイトを置いて学習できる&lt;/li&gt;
&lt;li&gt;簡単な問題の勾配を抑えることで、モデルの学習を難しい問題に集中させる&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="ゲート処理"&gt;ゲート処理&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;ブラックリスト方式ですべての不正入力を列挙するのではなく、条件を満たした入力だけを許可するホワイトリスト方式のゲートを設けた&lt;/li&gt;
&lt;li&gt;ゲート処理によって、OODや不適合入力が分類モデルまで到達する可能性を減らせる。その結果、False Positiveを抑えやすくなる&lt;/li&gt;
&lt;li&gt;一方で、本来はpositiveである入力まで棄却するとFalse Negativeが増える&lt;/li&gt;
&lt;li&gt;そのため、ゲートの厳しさとcoverageのトレードオフを評価する必要がある&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="oodスコアを算出する"&gt;OODスコアを算出する&lt;/h4&gt;
&lt;p&gt;分類モデルのsoftmax確率だけでなく、入力が分布外である可能性を表すOODスコアを算出した。&lt;/p&gt;
&lt;p&gt;OODスコアとしては、例えば次のようなものがある:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;MSP（Maximum Softmax Probability）
&lt;ul&gt;
&lt;li&gt;softmax出力の最大値（Top-1クラスの確率）を確信度スコアとして使う、最も単純な方法&lt;/li&gt;
&lt;li&gt;しかし、全クラスへの反応が弱い入力でも比率さえ偏っていれば高い値が出てしまう（相対的なため）&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Energy Score
&lt;ul&gt;
&lt;li&gt;softmaxを通す前のlogit（生の出力値）から計算するスコア&lt;/li&gt;
&lt;li&gt;logitの絶対的な大きさを反映して、「どのクラスにも強く反応していない入力」を検出しやすい&lt;/li&gt;
&lt;li&gt;IDならエネルギーが低く（負に大きく）、OODなら高くなる傾向&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;特徴空間における学習データからの距離&lt;/li&gt;
&lt;li&gt;複数モデル間の予測の不一致&lt;/li&gt;
&lt;li&gt;OOD検出専用モデルの出力&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OODスコアにしきい値を設定し、しきい値を超えた入力をOut of Scopeとして棄却する。&lt;/p&gt;
&lt;p&gt;ただし、OODスコアは必ずしもOODである確率ではない。また、学習分布から明確に離れたfar-OODは検出できても、IDに近いnear-OODは検出が難しい場合がある。&lt;/p&gt;
&lt;h4 id="マルチタスクで学習をする"&gt;マルチタスクで学習をする&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;マルチタスク学習（Multi-task learning; MTL）が 有効な場合もある&lt;/li&gt;
&lt;li&gt;補タスク（auxiliary tasks）の学習から得た教師信号をもとに興味がある主タスクの性能を向上することができるから&lt;/li&gt;
&lt;li&gt;あえて、gradeのheadもつけて学習させることによってauxiliary task learningができる&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="想定運用母集団に対する性能評価"&gt;想定運用母集団に対する性能評価&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;固定データセットではなくリアルでどの程度使えるかを判断するため点推定ではなくCIで判断する&lt;/li&gt;
&lt;li&gt;特に、固定済みモデルを、有限件のテスト標本で評価したことによる不確実性を測るのが目的&lt;/li&gt;
&lt;li&gt;そのため、次の指標のCIを測る
&lt;ul&gt;
&lt;li&gt;None Recallの片側下限&lt;/li&gt;
&lt;li&gt;各TargetクラスPrecisionの片側下限&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;また、要求水準を「None Recall 99%以上」、信頼水準を95%として、二項CIで出した&lt;/li&gt;
&lt;li&gt;ただし、CIが測るのは主に標本誤差で、分布シフトまではカバーしないので注意&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="自動学習"&gt;自動学習&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;シンプルに精度が低いサブクラスは学習データを増やす方法&lt;/li&gt;
&lt;li&gt;サブクラスのカバー率とサブクラス学習効率を求めて、それに対して精度を比較する&lt;/li&gt;
&lt;li&gt;精度が低いなら、そのサブクラスの割合を想定的に他のクラスより増やす&lt;/li&gt;
&lt;li&gt;これだけで自動学習が可能になる&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="対照学習"&gt;対照学習&lt;/h3&gt;
&lt;h4 id="対照学習とは"&gt;対照学習とは&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;教えるのが難しいものを、対照として用意して特別に学習させる方法&lt;/li&gt;
&lt;li&gt;つまり、ほぼ同じ条件の正解と誤答”を直接並べて、正解のスコアを必ず上にしろTrainする方法&lt;/li&gt;
&lt;li&gt;例えば、「たろう」を分類するために、「XXXたろう」と「XXXたらう」みたいなpositive/negativeのpairを用意して学習する&lt;/li&gt;
&lt;li&gt;普通の学習では、そのペア比較するべきペアだとわからないため、対照学習という方法がある&lt;/li&gt;
&lt;li&gt;そして、普通の学習とは別に、$score(Taro) &gt; score(near-miss) + margin$というLossを入れる&lt;/li&gt;
&lt;li&gt;実際は以下のような形
&lt;ul&gt;
&lt;li&gt;$ReLU(2 - (logit_{pos}-logit_{neg}))$&lt;/li&gt;
&lt;li&gt;ちなみに、ReLUは、$ReLU(x) = max(0,x)$という関数&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;つまり、positiveのlogitをnegativeのlogitより2以上高くしてくださいという事&lt;/li&gt;
&lt;li&gt;このPair Lossを使って学習をさせるイメージ&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="対照学習のpair-lossの具体例"&gt;対照学習のPair Lossの具体例&lt;/h4&gt;
&lt;p&gt;差が1だとlossは罰になる:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;positive logit = 3.0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;negative logit = 2.0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;loss = ReLU(2 -(3-2))= 1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;差が2.5だとlossは0になる（罰なし）:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;positive logit = 3.0
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;negative logit = 0.5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;loss = ReLU(2 -(3-0.5)) = ReLU(2-2.5) = ReLU(-0.5) = 0
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;つまり、差が開くと、「もう十分離れてる=学習できた」となるということ（この2がmargin）。&lt;/p&gt;
&lt;h4 id="bceとpair-lossの違い"&gt;BCEとPair Lossの違い&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;一般的に二値分類の損失関数はBCE（Binary Cross Entropy=二値交差エントロピー）を使う&lt;/li&gt;
&lt;li&gt;positiveである確率は、$L=−[y log_p+(1−y)log(1−p)]$となる&lt;/li&gt;
&lt;li&gt;つまりBCEは次の仕組み
&lt;ul&gt;
&lt;li&gt;y=1
&lt;ul&gt;
&lt;li&gt;$-log_p$で、p=1なら0でlossなし&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;y=0
&lt;ul&gt;
&lt;li&gt;$-log_(1-p)$で、p=0ならばlossなし&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;しかし、これは結局は、y=1のラベルならpは1に近づき、y=0のラベルならpは0に近づく事を学習するに過ぎない&lt;/li&gt;
&lt;li&gt;言い換えると以下を学習している
&lt;ul&gt;
&lt;li&gt;positiveには「1を出せ」&lt;/li&gt;
&lt;li&gt;negativeには「0を出せ」&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;他方、対照学習は場合分けせずに、統合して（2つを比較して距離を）学習している&lt;/li&gt;
&lt;li&gt;故に、対照学習は、次の力学が働く:
&lt;ul&gt;
&lt;li&gt;Positive logit ↑ 上げる&lt;/li&gt;
&lt;li&gt;near-miss logit ↓ 下げる&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Binary Cross Entropyではなく、ReLUを使う理由がこれ&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="推論"&gt;推論&lt;/h3&gt;
&lt;h4 id="userの入力を狭める"&gt;Userの入力を狭める&lt;/h4&gt;
&lt;p&gt;任意の入力を完全に扱うという要件を見直し、システムが対応する入力の範囲を明確にした。&lt;/p&gt;
&lt;p&gt;例えば、次のような条件を定義する。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;対応するデータ形式&lt;/li&gt;
&lt;li&gt;対応するクラス&lt;/li&gt;
&lt;li&gt;必要な画像品質&lt;/li&gt;
&lt;li&gt;許可するユーザーや利用環境&lt;/li&gt;
&lt;li&gt;モデルが判定してよい状況&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ユーザーごとに対象データを登録または学習し、その範囲だけを判定対象にする方法も考えられる。&lt;/p&gt;
&lt;p&gt;機械学習モデルの改善だけで安全性を確保しようとせず、モデルが動作してよい範囲をシステム側で定義することが重要だった。&lt;/p&gt;
&lt;h4 id="判定しきい値を上げる"&gt;判定しきい値を上げる&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;positiveと判定するしきい値を上げる方法は、False Positiveを抑えるための単純で有効な方法である&lt;/li&gt;
&lt;li&gt;一般には、しきい値を上げるとprecisionは上がりやすくなるが、recallとcoverageは下がる&lt;/li&gt;
&lt;li&gt;また、DNNは未知入力に対しても高いsoftmaxスコアを出すことがある。そのため、分類確率のしきい値だけに依存せず、OODスコアやゲート処理と組み合わせる必要がある&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id="相対的な確信度を利用する"&gt;相対的な確信度を利用する&lt;/h4&gt;
&lt;p&gt;一つのクラスの確信度だけでなく、複数クラス間のスコアを比較した。&lt;/p&gt;
&lt;p&gt;例えば、次のような値を利用できる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Top-1とTop-2の確率差&lt;/li&gt;
&lt;li&gt;Top-1とTop-2のlogit差&lt;/li&gt;
&lt;li&gt;予測分布のエントロピー&lt;/li&gt;
&lt;li&gt;複数モデル間の予測のばらつき&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;同じTop-1確率でも、他のクラスと僅差の場合と、一つのクラスだけが突出している場合では意味が異なる。&lt;/p&gt;
&lt;p&gt;ただし、相対的な確信度は主に既知クラス間の曖昧さを表す指標であり、それだけでOODを検出できるわけではない。OOD入力に対して一つのクラスが突出することもある。&lt;/p&gt;
&lt;h4 id="アンサンブルで判定する"&gt;アンサンブルで判定する&lt;/h4&gt;
&lt;p&gt;複数のモデルを利用し、予測結果やモデル間の不一致を判定材料にした。&lt;/p&gt;
&lt;p&gt;例えば、次のような方法が考えられる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;複数モデルの平均確率を使う&lt;/li&gt;
&lt;li&gt;すべてのモデルがpositiveと判定した場合だけ受理する&lt;/li&gt;
&lt;li&gt;モデル間の予測分散が大きい場合は棄却する&lt;/li&gt;
&lt;li&gt;異なる特徴や学習データを使ったモデルを組み合わせる&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;アンサンブルは予測性能や不確実性推定を改善する可能性がある。&lt;/p&gt;
&lt;p&gt;ただし、複数モデルが同じ学習データや特徴に依存している場合、同じ入力に対して同時に誤る可能性がある。アンサンブルだけで安全性を保証できるわけではない。&lt;/p&gt;
&lt;h4 id="selective-classification"&gt;Selective Classification&lt;/h4&gt;
&lt;p&gt;確信度が不十分な場合に、モデルが無理に分類せず棄却できる仕組みを追加した。&lt;/p&gt;
&lt;p&gt;棄却された入力については、次のようなフローへ移す。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ユーザーに確認する&lt;/li&gt;
&lt;li&gt;管理者によるレビューへ送る&lt;/li&gt;
&lt;li&gt;より高精度なモデルで再判定する&lt;/li&gt;
&lt;li&gt;安全側のデフォルト処理を実行する&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;これはSelective ClassificationまたはClassification with a Reject Optionと呼ばれる考え方に近い。&lt;/p&gt;
&lt;p&gt;棄却を増やせば受理した予測の精度は高めやすいが、自動処理できるcoverageは下がる。そのため、誤り率とcoverageの両方を評価する必要がある。&lt;/p&gt;
&lt;p&gt;また、ユーザーへの確認を追加しても、ユーザー自身が正解を判断できなければ安全性は保証されない。誰が何を根拠に確認するかまで設計する必要がある。&lt;/p&gt;
&lt;h4 id="事前確率を考慮する"&gt;事前確率を考慮する&lt;/h4&gt;
&lt;p&gt;クラスの発生頻度が学習時と運用時で異なる場合、運用環境の事前確率を考慮する方法がある。&lt;/p&gt;
&lt;p&gt;Label Shiftを仮定できる場合、推論結果は次のように補正できる。&lt;/p&gt;
$$
P_{\mathrm{test}}(y \mid x)
\propto
P_{\mathrm{train}}(y \mid x)
\frac{P_{\mathrm{test}}(y)}
{P_{\mathrm{train}}(y)}
$$&lt;p&gt;ただし、この補正は主に次の仮定が成立する場合に利用できる。&lt;/p&gt;
$$
P_{\mathrm{test}}(x \mid y) = P_{\mathrm{train}}(x \mid y)
$$&lt;p&gt;運用環境の事前確率を正しく推定できない場合や、Concept Shiftも発生している場合には、単純な補正が逆効果になる可能性がある。&lt;/p&gt;
&lt;p&gt;また、False PositiveとFalse Negativeのコスト差を反映したい場合は、事前確率補正とは分けて、コストを考慮した判定しきい値を設計する。&lt;/p&gt;
&lt;h4 id="conformal-prediction"&gt;Conformal Prediction&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;モデルフリーのConformal Predictionでしきい値や予測集合を統計的に較正し、自動判定、棄却を決めることも可能&lt;/li&gt;
&lt;li&gt;各入力に対して予測集合を出し、長期的な誤り率が目標値以下になるよう較正する事が可能になる&lt;/li&gt;
&lt;li&gt;普通の方法： 「0.9なら十分自信が高そう」、Conformal：「過去のNegativeの99%を誤判定しなかった境界を使う」というイメージ&lt;/li&gt;
&lt;li&gt;その前提として、キャリブレーションデータと本番データが同じような分布であることを前提にした長期的・集団的な保証ということ&lt;/li&gt;
&lt;li&gt;UQ解析の一種&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="モデル選定"&gt;モデル選定&lt;/h3&gt;
&lt;h4 id="タスクに適したモデルへ変更する"&gt;タスクに適したモデルへ変更する&lt;/h4&gt;
&lt;p&gt;分類対象同士の違いが細かい場合は、Fine-grained classificationに適したモデルや学習方法を採用する。&lt;/p&gt;
&lt;p&gt;例えば、車とペットボトルのような上位カテゴリ間の分類に比べ、タカとワシのように同じ上位カテゴリ内の下位カテゴリを区別する分類では、細かな局所特徴が重要になる。&lt;/p&gt;
&lt;p&gt;この場合、次のような方法が考えられる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;高解像度の入力を使う&lt;/li&gt;
&lt;li&gt;局所特徴や部位に注目するモデルを使う&lt;/li&gt;
&lt;li&gt;Metric LearningやContrastive Learningを使う&lt;/li&gt;
&lt;li&gt;階層分類を行う&lt;/li&gt;
&lt;li&gt;タスク専用の特徴抽出器を利用する&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Fine-grained classificationへの対応は既知クラス間の識別性能を改善する方法であり、それ自体がOOD対策になるわけではない。&lt;/p&gt;
&lt;h4 id="転移学習を利用する"&gt;転移学習を利用する&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;大規模データで事前学習されたモデルを利用し、対象タスクへFine-tuningする&lt;/li&gt;
&lt;li&gt;事前学習によって得られた汎用的な表現は、限られた学習データだけで一から学習する場合よりも、分類性能や分布シフトへの頑健性を改善する可能性がある&lt;/li&gt;
&lt;li&gt;LLMにおけるLoRAは、事前学習済みモデルを少ない追加パラメータでFine-tuningするParameter-Efficient Fine-Tuningの一手法である。LoRA自体が転移学習と同義というわけではない&lt;/li&gt;
&lt;li&gt;また、転移学習を行ってもOODを必ず検出できるわけではないため、OODスコアや棄却処理とは別に考える必要がある&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="feedback"&gt;feedback&lt;/h3&gt;
&lt;h4 id="運用中の監視と再学習"&gt;運用中の監視と再学習&lt;/h4&gt;
&lt;p&gt;開発時に想定できなかった入力は、運用開始後にも発生する。&lt;/p&gt;
&lt;p&gt;そのため、次の情報を継続的に収集する。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;棄却された入力&lt;/li&gt;
&lt;li&gt;高い確信度で誤判定した入力&lt;/li&gt;
&lt;li&gt;モデル間で判定が分かれた入力&lt;/li&gt;
&lt;li&gt;ユーザーが訂正した入力&lt;/li&gt;
&lt;li&gt;時間経過による入力分布の変化&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;収集したデータからhard negativeを発掘し、評価データの追加や再学習に利用する。&lt;/p&gt;
&lt;p&gt;ただし、同じデータをしきい値調整と最終評価の両方に使うと、性能を過大評価する可能性がある。学習用、検証用、最終評価用のデータは分ける必要がある。&lt;/p&gt;
&lt;h2 id="check項目まとめ"&gt;Check項目まとめ&lt;/h2&gt;
&lt;p&gt;量が多いので、AIにSVGにしてもらった。&lt;/p&gt;
&lt;p&gt;&lt;img src="https://www.m1ke.org/p/%E5%88%86%E5%B8%83%E5%A4%96%E3%83%87%E3%83%BC%E3%82%BF%E3%82%92%E5%90%AB%E3%82%80%E5%88%86%E9%A1%9E%E3%81%A7false-positive%E3%82%92%E6%8A%91%E3%81%88%E3%82%8B%E6%96%B9%E6%B3%95/fp_ood_diagnostic_flow_complete_no_arrows.svg"
loading="lazy"
&gt;&lt;/p&gt;
&lt;p&gt;また、簡潔にいうとこれらは最低限でもやったほうがいい:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;まず分母が同じ事&lt;/li&gt;
&lt;li&gt;決定的なアルゴであること&lt;/li&gt;
&lt;li&gt;非決定的なら信頼区間を出すこと&lt;/li&gt;
&lt;li&gt;点推定ではなく信頼区間していること&lt;/li&gt;
&lt;li&gt;それらを細かなデータ区分でやること&lt;/li&gt;
&lt;li&gt;サンプル数があること&lt;/li&gt;
&lt;li&gt;因果推論してること&lt;/li&gt;
&lt;li&gt;統計的に有意なこと&lt;/li&gt;
&lt;li&gt;論理的矛盾がないこと&lt;/li&gt;
&lt;li&gt;証明されていること（truth and proof ）&lt;/li&gt;
&lt;li&gt;事前の仮説との整合性があること&lt;/li&gt;
&lt;li&gt;交差検証されてること&lt;/li&gt;
&lt;li&gt;ホールドアウトで確認してること&lt;/li&gt;
&lt;li&gt;未検証のデータセットを使ってること&lt;/li&gt;
&lt;li&gt;ドメインシフトが起こってないこと&lt;/li&gt;
&lt;li&gt;しきい値が一つじゃないこと（aucなどを使用）&lt;/li&gt;
&lt;li&gt;seedを変えたときの再現性があること&lt;/li&gt;
&lt;li&gt;欠陥変数がないこと&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="まとめ"&gt;まとめ&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ID/OODとpositive/negativeは別の概念として扱う必要がある&lt;/li&gt;
&lt;li&gt;DNNは未知入力に対しても高い確信度を出すことがあるため、分類確率のしきい値だけでは十分ではない&lt;/li&gt;
&lt;li&gt;False Positiveを抑えるには、入力ゲート、Noneクラス、疑似OOD、hard negative mining、OODスコア、アンサンブル、棄却などを組み合わせる必要がある&lt;/li&gt;
&lt;li&gt;Noneクラスや転移学習は有効な場合があるが、単独で未知入力全般に対応できるわけではない&lt;/li&gt;
&lt;li&gt;安全性が重要なシステムでは、確信度が不十分な入力を無理に分類せず、人間による確認や安全側の処理へ移す設計が重要である&lt;/li&gt;
&lt;li&gt;有限の評価データから、将来のあらゆる入力に対するFP 0件を保証することはできない&lt;/li&gt;
&lt;li&gt;FPだけを減らすとrecallやcoverageが低下するため、これらのトレードオフも評価する必要がある&lt;/li&gt;
&lt;li&gt;OODへの対処は単一のモデルや手法で完結するものではなく、入力制約、モデル、棄却、監視、再学習を含むシステム全体で設計する必要がある&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OOD対策の本質は、すべての入力を無理に分類することではなく、モデルが判断してよい範囲を定め、判断できない入力を安全に棄却すること。&lt;/p&gt;
&lt;h2 id="参考文献"&gt;参考文献&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://arxiv.org/abs/1610.02136" target="_blank" rel="noopener"
&gt;A Baseline for Detecting Misclassified and Out-of-Distribution Examples in Neural Networks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://arxiv.org/abs/1812.04606" target="_blank" rel="noopener"
&gt;Deep Anomaly Detection with Outlier Exposure&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://papers.nips.cc/paper/2020/hash/f5496252609c43eb8a3d147ab9b9c006-Abstract.html" target="_blank" rel="noopener"
&gt;Energy-based Out-of-distribution Detection&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://arxiv.org/abs/2006.15207" target="_blank" rel="noopener"
&gt;ATOM: Robustifying Out-of-distribution Detection Using Outlier Mining&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://papers.nips.cc/paper_files/paper/2017/hash/9ef2ed4b7fd2c810847ffa5fa85bce38-Abstract.html" target="_blank" rel="noopener"
&gt;Simple and Scalable Predictive Uncertainty Estimation using Deep Ensembles&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://papers.nips.cc/paper/2019/hash/8558cb408c1d76621371888657d2eb1d-Abstract.html" target="_blank" rel="noopener"
&gt;Can You Trust Your Model&amp;rsquo;s Uncertainty? Evaluating Predictive Uncertainty Under Dataset Shift&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://proceedings.mlr.press/v97/geifman19a" target="_blank" rel="noopener"
&gt;SelectiveNet: A Deep Neural Network with an Integrated Reject Option&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://proceedings.mlr.press/v80/lipton18a" target="_blank" rel="noopener"
&gt;Detecting and Correcting for Label Shift with Black Box Predictors&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://arxiv.org/abs/2406.14551" target="_blank" rel="noopener"
&gt;Advancing Fine-Grained Classification by Structure and Subject Preserving Augmentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://iblog.ridge-i.com/entry/2021/03/10/110000" target="_blank" rel="noopener"
&gt;トップカンファレンスにおけるデータセットシフトと機械学習&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://qiita.com/koreyou/items/3dc56a1573924f1178b2" target="_blank" rel="noopener"
&gt;論文紹介: Adapting Auxiliary Losses Using Gradient Similarity #機械学習 - Qiita&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://qiita.com/udi_/items/8bc1b8c1854340c30216" target="_blank" rel="noopener"
&gt;Conformal Predictionで不確実性を定量化する #機械学習 - Qiita&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item></channel></rss>