├── .github └── workflows │ └── test.yml ├── README.md ├── day1 ├── .env_template ├── .gitignore ├── 01_streamlit_UI │ ├── app.py │ └── requirements.txt ├── 02_streamlit_app │ ├── app.py │ ├── config.py │ ├── data.py │ ├── database.py │ ├── llm.py │ ├── metrics.py │ ├── requirements.txt │ └── ui.py ├── 03_FastAPI │ ├── app.py │ ├── python-client.py │ └── requirements.txt ├── README.md └── day1_practice.ipynb ├── day3 ├── README.md ├── ai_engineering_03.ipynb ├── ai_engineering_03_T4.ipynb └── data │ ├── LLM2024_day4.txt │ ├── LLM2024_day4_raw.txt │ ├── llm04_eng.json │ └── llm04_eng_nofix.json └── day5 ├── .gitignore ├── README.md ├── requirements.txt ├── 演習1 ├── data │ └── Titanic.csv ├── main.py ├── models │ └── titanic_model.pkl └── pipeline.py ├── 演習2 ├── black_check.py ├── data │ ├── Titanic.csv │ └── Titanic_error.csv ├── main.py └── models │ └── titanic_model.pkl └── 演習3 ├── data └── Titanic.csv ├── models └── titanic_model.pkl └── tests ├── test_data.py └── test_model.py /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: ML Pipeline CI 2 | 3 | on: 4 | # push: 5 | # branches: [ main, master ] 6 | pull_request: 7 | branches: [ main, master ] 8 | 9 | jobs: 10 | test: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | 15 | - name: Set up Python 16 | uses: actions/setup-python@v4 17 | with: 18 | python-version: '3.10' 19 | 20 | - name: Install dependencies 21 | run: | 22 | python -m pip install --upgrade pip 23 | pip install pytest great_expectations pandas scikit-learn flake8 black mypy pytest-cov 24 | if [ -f requirements.txt ]; then pip install -r requirements.txt; fi 25 | 26 | - name: Lint with flake8 27 | run: | 28 | flake8 day5/演習3 --count --select=E9,F63,F7,F82 --show-source --statistics 29 | flake8 day5/演習3 --count --exit-zero --max-complexity=10 --max-line-length=88 --statistics 30 | 31 | - name: Format check with black 32 | run: | 33 | black --check day5/演習3 34 | 35 | - name: Run data tests 36 | run: | 37 | pytest day5/演習3/tests/test_data.py -v 38 | 39 | - name: Run model tests 40 | run: | 41 | pytest day5/演習3/tests/test_model.py -v 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lecture-ai-engineering 2 | AIエンジニアリング実践講座(公開用リポジトリ) 3 | -------------------------------------------------------------------------------- /day1/.env_template: -------------------------------------------------------------------------------- 1 | HUGGINGFACE_TOKEN="hf-********" 2 | NGROK_TOKEN="********" -------------------------------------------------------------------------------- /day1/.gitignore: -------------------------------------------------------------------------------- 1 | # 演習で作成されるシークレットファイルとdbファイル 2 | **/secrets.toml 3 | **/secret.toml 4 | **/chat_feedback.db 5 | 6 | # Byte-compiled / optimized / DLL files 7 | __pycache__/ 8 | *.py[cod] 9 | *$py.class 10 | 11 | # C extensions 12 | *.so 13 | 14 | # Distribution / packaging 15 | .Python 16 | build/ 17 | develop-eggs/ 18 | dist/ 19 | downloads/ 20 | eggs/ 21 | .eggs/ 22 | lib/ 23 | lib64/ 24 | parts/ 25 | sdist/ 26 | var/ 27 | wheels/ 28 | share/python-wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | MANIFEST 33 | 34 | # PyInstaller 35 | # Usually these files are written by a python script from a template 36 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 37 | *.manifest 38 | *.spec 39 | 40 | # Installer logs 41 | pip-log.txt 42 | pip-delete-this-directory.txt 43 | 44 | # Unit test / coverage reports 45 | htmlcov/ 46 | .tox/ 47 | .nox/ 48 | .coverage 49 | .coverage.* 50 | .cache 51 | nosetests.xml 52 | coverage.xml 53 | *.cover 54 | *.py,cover 55 | .hypothesis/ 56 | .pytest_cache/ 57 | cover/ 58 | 59 | # Translations 60 | *.mo 61 | *.pot 62 | 63 | # Django stuff: 64 | *.log 65 | local_settings.py 66 | db.sqlite3 67 | db.sqlite3-journal 68 | 69 | # Flask stuff: 70 | instance/ 71 | .webassets-cache 72 | 73 | # Scrapy stuff: 74 | .scrapy 75 | 76 | # Sphinx documentation 77 | docs/_build/ 78 | 79 | # PyBuilder 80 | .pybuilder/ 81 | target/ 82 | 83 | # Jupyter Notebook 84 | .ipynb_checkpoints 85 | 86 | # IPython 87 | profile_default/ 88 | ipython_config.py 89 | 90 | # pyenv 91 | # For a library or package, you might want to ignore these files since the code is 92 | # intended to run in multiple environments; otherwise, check them in: 93 | # .python-version 94 | 95 | # pipenv 96 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 97 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 98 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 99 | # install all needed dependencies. 100 | #Pipfile.lock 101 | 102 | # UV 103 | # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. 104 | # This is especially recommended for binary packages to ensure reproducibility, and is more 105 | # commonly ignored for libraries. 106 | #uv.lock 107 | 108 | # poetry 109 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 110 | # This is especially recommended for binary packages to ensure reproducibility, and is more 111 | # commonly ignored for libraries. 112 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 113 | #poetry.lock 114 | 115 | # pdm 116 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 117 | #pdm.lock 118 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 119 | # in version control. 120 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 121 | .pdm.toml 122 | .pdm-python 123 | .pdm-build/ 124 | 125 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 126 | __pypackages__/ 127 | 128 | # Celery stuff 129 | celerybeat-schedule 130 | celerybeat.pid 131 | 132 | # SageMath parsed files 133 | *.sage.py 134 | 135 | # Environments 136 | .env 137 | .venv 138 | env/ 139 | venv/ 140 | ENV/ 141 | env.bak/ 142 | venv.bak/ 143 | 144 | # Spyder project settings 145 | .spyderproject 146 | .spyproject 147 | 148 | # Rope project settings 149 | .ropeproject 150 | 151 | # mkdocs documentation 152 | /site 153 | 154 | # mypy 155 | .mypy_cache/ 156 | .dmypy.json 157 | dmypy.json 158 | 159 | # Pyre type checker 160 | .pyre/ 161 | 162 | # pytype static type analyzer 163 | .pytype/ 164 | 165 | # Cython debug symbols 166 | cython_debug/ 167 | 168 | # PyCharm 169 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 170 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 171 | # and can be added to the global gitignore or merged into this file. For a more nuclear 172 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 173 | #.idea/ 174 | 175 | # Ruff stuff: 176 | .ruff_cache/ 177 | 178 | # PyPI configuration file 179 | .pypirc 180 | -------------------------------------------------------------------------------- /day1/01_streamlit_UI/app.py: -------------------------------------------------------------------------------- 1 | import streamlit as st 2 | import pandas as pd 3 | import numpy as np 4 | import time 5 | 6 | # ============================================ 7 | # ページ設定 8 | # ============================================ 9 | # st.set_page_config( 10 | # page_title="Streamlit デモ", 11 | # layout="wide", 12 | # initial_sidebar_state="expanded" 13 | # ) 14 | 15 | # ============================================ 16 | # タイトルと説明 17 | # ============================================ 18 | st.title("Streamlit 初心者向けデモ") 19 | st.markdown("### コメントを解除しながらStreamlitの機能を学びましょう") 20 | st.markdown("このデモコードでは、コメントアウトされた部分を順番に解除しながらUIの変化を確認できます。") 21 | 22 | # ============================================ 23 | # サイドバー 24 | # ============================================ 25 | st.sidebar.header("デモのガイド") 26 | st.sidebar.info("コードのコメントを解除して、Streamlitのさまざまな機能を確認しましょう。") 27 | 28 | # ============================================ 29 | # 基本的なUI要素 30 | # ============================================ 31 | st.header("基本的なUI要素") 32 | 33 | # テキスト入力 34 | st.subheader("テキスト入力") 35 | name = st.text_input("あなたの名前", "ゲスト") 36 | st.write(f"こんにちは、{name}さん!") 37 | 38 | # ボタン 39 | # st.subheader("ボタン") 40 | # if st.button("クリックしてください"): 41 | # st.success("ボタンがクリックされました!") 42 | 43 | # チェックボックス 44 | # st.subheader("チェックボックス") 45 | # if st.checkbox("チェックを入れると追加コンテンツが表示されます"): 46 | # st.info("これは隠れたコンテンツです!") 47 | 48 | # スライダー 49 | # st.subheader("スライダー") 50 | # age = st.slider("年齢", 0, 100, 25) 51 | # st.write(f"あなたの年齢: {age}") 52 | 53 | # セレクトボックス 54 | # st.subheader("セレクトボックス") 55 | # option = st.selectbox( 56 | # "好きなプログラミング言語は?", 57 | # ["Python", "JavaScript", "Java", "C++", "Go", "Rust"] 58 | # ) 59 | # st.write(f"あなたは{option}を選びました") 60 | 61 | # ============================================ 62 | # レイアウト 63 | # ============================================ 64 | # st.header("レイアウト") 65 | 66 | # カラム 67 | # st.subheader("カラムレイアウト") 68 | # col1, col2 = st.columns(2) 69 | # with col1: 70 | # st.write("これは左カラムです") 71 | # st.number_input("数値を入力", value=10) 72 | # with col2: 73 | # st.write("これは右カラムです") 74 | # st.metric("メトリクス", "42", "2%") 75 | 76 | # タブ 77 | # st.subheader("タブ") 78 | # tab1, tab2 = st.tabs(["第1タブ", "第2タブ"]) 79 | # with tab1: 80 | # st.write("これは第1タブの内容です") 81 | # with tab2: 82 | # st.write("これは第2タブの内容です") 83 | 84 | # エクスパンダー 85 | # st.subheader("エクスパンダー") 86 | # with st.expander("詳細を表示"): 87 | # st.write("これはエクスパンダー内の隠れたコンテンツです") 88 | # st.code("print('Hello, Streamlit!')") 89 | 90 | # ============================================ 91 | # データ表示 92 | # ============================================ 93 | # st.header("データの表示") 94 | 95 | # サンプルデータフレームを作成 96 | # df = pd.DataFrame({ 97 | # '名前': ['田中', '鈴木', '佐藤', '高橋', '伊藤'], 98 | # '年齢': [25, 30, 22, 28, 33], 99 | # '都市': ['東京', '大阪', '福岡', '札幌', '名古屋'] 100 | # }) 101 | 102 | # データフレーム表示 103 | # st.subheader("データフレーム") 104 | # st.dataframe(df, use_container_width=True) 105 | 106 | # テーブル表示 107 | # st.subheader("テーブル") 108 | # st.table(df) 109 | 110 | # メトリクス表示 111 | # st.subheader("メトリクス") 112 | # col1, col2, col3 = st.columns(3) 113 | # col1.metric("温度", "23°C", "1.5°C") 114 | # col2.metric("湿度", "45%", "-5%") 115 | # col3.metric("気圧", "1013hPa", "0.1hPa") 116 | 117 | # ============================================ 118 | # グラフ表示 119 | # ============================================ 120 | # st.header("グラフの表示") 121 | 122 | # ラインチャート 123 | # st.subheader("ラインチャート") 124 | # chart_data = pd.DataFrame( 125 | # np.random.randn(20, 3), 126 | # columns=['A', 'B', 'C']) 127 | # st.line_chart(chart_data) 128 | 129 | # バーチャート 130 | # st.subheader("バーチャート") 131 | # chart_data = pd.DataFrame({ 132 | # 'カテゴリ': ['A', 'B', 'C', 'D'], 133 | # '値': [10, 25, 15, 30] 134 | # }).set_index('カテゴリ') 135 | # st.bar_chart(chart_data) 136 | 137 | # ============================================ 138 | # インタラクティブ機能 139 | # ============================================ 140 | # st.header("インタラクティブ機能") 141 | 142 | # プログレスバー 143 | # st.subheader("プログレスバー") 144 | # progress = st.progress(0) 145 | # if st.button("進捗をシミュレート"): 146 | # for i in range(101): 147 | # time.sleep(0.01) 148 | # progress.progress(i / 100) 149 | # st.balloons() 150 | 151 | # ファイルアップロード 152 | # st.subheader("ファイルアップロード") 153 | # uploaded_file = st.file_uploader("ファイルをアップロード", type=["csv", "txt"]) 154 | # if uploaded_file is not None: 155 | # # ファイルのデータを表示 156 | # bytes_data = uploaded_file.getvalue() 157 | # st.write(f"ファイルサイズ: {len(bytes_data)} bytes") 158 | # 159 | # # CSVの場合はデータフレームとして読み込む 160 | # if uploaded_file.name.endswith('.csv'): 161 | # df = pd.read_csv(uploaded_file) 162 | # st.write("CSVデータのプレビュー:") 163 | # st.dataframe(df.head()) 164 | 165 | # ============================================ 166 | # カスタマイズ 167 | # ============================================ 168 | # st.header("スタイルのカスタマイズ") 169 | 170 | # カスタムCSS 171 | # st.markdown(""" 172 | # 179 | # """, unsafe_allow_html=True) 180 | # 181 | # st.markdown('

これはカスタムCSSでスタイリングされたテキストです!

', unsafe_allow_html=True) 182 | 183 | # ============================================ 184 | # デモの使用方法 185 | # ============================================ 186 | st.divider() 187 | st.subheader("このデモの使い方") 188 | st.markdown(""" 189 | 1. コードエディタでコメントアウトされた部分を見つけます(#で始まる行) 190 | 2. 確認したい機能のコメントを解除します(先頭の#を削除) 191 | 3. 変更を保存して、ブラウザで結果を確認します 192 | 4. 様々な組み合わせを試して、UIがどのように変化するか確認しましょう 193 | """) 194 | 195 | st.code(""" 196 | # コメントアウトされた例: 197 | # if st.button("クリックしてください"): 198 | # st.success("ボタンがクリックされました!") 199 | 200 | # コメントを解除した例: 201 | if st.button("クリックしてください"): 202 | st.success("ボタンがクリックされました!") 203 | """) -------------------------------------------------------------------------------- /day1/01_streamlit_UI/requirements.txt: -------------------------------------------------------------------------------- 1 | streamlit 2 | pandas 3 | numpy 4 | pyngrok -------------------------------------------------------------------------------- /day1/02_streamlit_app/app.py: -------------------------------------------------------------------------------- 1 | # app.py 2 | import streamlit as st 3 | import ui # UIモジュール 4 | import llm # LLMモジュール 5 | import database # データベースモジュール 6 | import metrics # 評価指標モジュール 7 | import data # データモジュール 8 | import torch 9 | from transformers import pipeline 10 | from config import MODEL_NAME 11 | from huggingface_hub import HfFolder 12 | 13 | # --- アプリケーション設定 --- 14 | st.set_page_config(page_title="Gemma Chatbot", layout="wide") 15 | 16 | # --- 初期化処理 --- 17 | # NLTKデータのダウンロード(初回起動時など) 18 | metrics.initialize_nltk() 19 | 20 | # データベースの初期化(テーブルが存在しない場合、作成) 21 | database.init_db() 22 | 23 | # データベースが空ならサンプルデータを投入 24 | data.ensure_initial_data() 25 | 26 | # LLMモデルのロード(キャッシュを利用) 27 | # モデルをキャッシュして再利用 28 | @st.cache_resource 29 | def load_model(): 30 | """LLMモデルをロードする""" 31 | try: 32 | device = "cuda" if torch.cuda.is_available() else "cpu" 33 | st.info(f"Using device: {device}") # 使用デバイスを表示 34 | pipe = pipeline( 35 | "text-generation", 36 | model=MODEL_NAME, 37 | model_kwargs={"torch_dtype": torch.bfloat16}, 38 | device=device 39 | ) 40 | st.success(f"モデル '{MODEL_NAME}' の読み込みに成功しました。") 41 | return pipe 42 | except Exception as e: 43 | st.error(f"モデル '{MODEL_NAME}' の読み込みに失敗しました: {e}") 44 | st.error("GPUメモリ不足の可能性があります。不要なプロセスを終了するか、より小さいモデルの使用を検討してください。") 45 | return None 46 | pipe = llm.load_model() 47 | 48 | # --- Streamlit アプリケーション --- 49 | st.title("🤖 Gemma 2 Chatbot with Feedback") 50 | st.write("Gemmaモデルを使用したチャットボットです。回答に対してフィードバックを行えます。") 51 | st.markdown("---") 52 | 53 | # --- サイドバー --- 54 | st.sidebar.title("ナビゲーション") 55 | # セッション状態を使用して選択ページを保持 56 | if 'page' not in st.session_state: 57 | st.session_state.page = "チャット" # デフォルトページ 58 | 59 | page = st.sidebar.radio( 60 | "ページ選択", 61 | ["チャット", "履歴閲覧", "サンプルデータ管理"], 62 | key="page_selector", 63 | index=["チャット", "履歴閲覧", "サンプルデータ管理"].index(st.session_state.page), # 現在のページを選択状態にする 64 | on_change=lambda: setattr(st.session_state, 'page', st.session_state.page_selector) # 選択変更時に状態を更新 65 | ) 66 | 67 | 68 | # --- メインコンテンツ --- 69 | if st.session_state.page == "チャット": 70 | if pipe: 71 | ui.display_chat_page(pipe) 72 | else: 73 | st.error("チャット機能を利用できません。モデルの読み込みに失敗しました。") 74 | elif st.session_state.page == "履歴閲覧": 75 | ui.display_history_page() 76 | elif st.session_state.page == "サンプルデータ管理": 77 | ui.display_data_page() 78 | 79 | # --- フッターなど(任意) --- 80 | st.sidebar.markdown("---") 81 | st.sidebar.info("開発者: [Your Name]") -------------------------------------------------------------------------------- /day1/02_streamlit_app/config.py: -------------------------------------------------------------------------------- 1 | # config.py 2 | DB_FILE = "chat_feedback.db" 3 | MODEL_NAME = "google/gemma-2-2b-jpn-it" -------------------------------------------------------------------------------- /day1/02_streamlit_app/data.py: -------------------------------------------------------------------------------- 1 | # data.py 2 | import streamlit as st 3 | from datetime import datetime 4 | from database import save_to_db, get_db_count # DB操作関数をインポート 5 | 6 | # サンプルデータのリスト 7 | SAMPLE_QUESTIONS_DATA = [ 8 | { 9 | "question": "Pythonのリスト内包表記とは何ですか?", 10 | "answer": "リスト内包表記は、既存のリストから新しいリストを作成するためのPythonの構文です。通常のfor文よりも簡潔に記述でき、パフォーマンスも向上する場合があります。", 11 | "correct_answer": "Pythonのリスト内包表記は、リストを簡潔に作成するための構文で、`[expression for item in iterable if condition]`の形式で書きます。通常のforループよりも短く書けて、実行速度も速い場合があります。", 12 | "feedback": "部分的に正確: 基本的な説明は正しいですが、具体的な構文例が示されていません", 13 | "is_correct": 0.5, 14 | "response_time": 1.2 15 | }, 16 | { 17 | "question": "機械学習における過学習とは?", 18 | "answer": "過学習(オーバーフィッティング)とは、機械学習モデルが訓練データに対して過度に適合し、新しいデータに対する汎化性能が低下する現象です。", 19 | "correct_answer": "過学習(オーバーフィッティング)は、モデルがトレーニングデータに過度に適合し、未知のデータに対する予測性能が低下する現象です。モデルが訓練データのノイズまで学習してしまうことが原因です。", 20 | "feedback": "正確: 過学習の本質をよく捉えています", 21 | "is_correct": 1.0, # 整数ではなく浮動小数点数で統一 22 | "response_time": 1.5 23 | }, 24 | # ... (他のサンプルデータも同様に追加) ... 25 | { 26 | "question": "量子コンピュータの基本原理は?", 27 | "answer": "量子コンピュータは量子力学の原理に基づいて動作します。従来のビットの代わりに量子ビット(キュービット)を使用し、重ね合わせと量子もつれの特性により並列計算を実現します。", 28 | "correct_answer": "量子コンピュータは量子力学的現象を利用した計算機で、従来のビットではなく量子ビット(キュービット)を使用します。キュービットは重ね合わせ状態をとることができ、複数の状態を同時に表現できます。また量子もつれにより、従来のコンピュータでは困難な特定の問題を効率的に解くことができます。", 29 | "feedback": "部分的に正確: 基本概念は正しいですが、詳細な説明が不足しています", 30 | "is_correct": 0.5, 31 | "response_time": 2.1 32 | }, 33 | { 34 | "question": "Streamlitとは何ですか?", 35 | "answer": "Streamlitは、Pythonで書かれたデータサイエンスやAIアプリケーションを簡単に作成するためのオープンソースフレームワークです。数行のコードでインタラクティブなWebアプリを作成できます。", 36 | "correct_answer": "Streamlitは、データサイエンティストやAIエンジニアがPythonを使って簡単にWebアプリケーションを構築できるフレームワークです。少ないコード量で、インタラクティブなダッシュボードやデータ可視化アプリケーションを作成できます。", 37 | "feedback": "正確: Streamlitの基本概念と利点をよく説明しています", 38 | "is_correct": 1.0, 39 | "response_time": 0.9 40 | }, 41 | { 42 | "question": "ブロックチェーンの仕組みを説明してください", 43 | "answer": "ブロックチェーンは、分散型台帳技術の一つで、データをブロックに格納し、暗号技術でリンクして改ざん防止を実現します。各ブロックには前のブロックのハッシュ値が含まれ、チェーン状に連結されています。", 44 | "correct_answer": "ブロックチェーンは分散型台帳技術で、データブロックが暗号学的にリンクされた構造です。各ブロックには取引データとタイムスタンプ、前ブロックのハッシュ値が含まれます。分散型ネットワークでコンセンサスアルゴリズムにより検証され、改ざんが極めて困難なシステムを実現しています。", 45 | "feedback": "部分的に正確: 基本的な説明はありますが、コンセンサスメカニズムについての言及がありません", 46 | "is_correct": 0.5, 47 | "response_time": 1.8 48 | }, 49 | { 50 | "question": "ディープラーニングとは何ですか?", 51 | "answer": "ディープラーニングは、複数の層からなるニューラルネットワークを用いた機械学習手法です。画像認識や自然言語処理など複雑なタスクに優れています。", 52 | "correct_answer": "ディープラーニングは多層ニューラルネットワークを使用した機械学習の一種で、特徴抽出を自動的に行う能力があります。画像認識、自然言語処理、音声認識などの複雑なタスクで革命的な成果を上げており、大量のデータと計算リソースを活用して従来の手法を超える性能を実現しています。", 53 | "feedback": "部分的に正確: 基本的な定義は正しいですが、詳細な説明が不足しています", 54 | "is_correct": 0.5, 55 | "response_time": 1.3 56 | }, 57 | { 58 | "question": "SQLインジェクションとは何ですか?", 59 | "answer": "SQLインジェクションは、Webアプリケーションの脆弱性を悪用して不正なSQLクエリを実行させる攻撃手法です。ユーザー入力を適切に検証・サニタイズしないことで発生します。", 60 | "correct_answer": "SQLインジェクションは、Webアプリケーションのセキュリティ脆弱性を悪用した攻撃手法で、攻撃者がユーザー入力フィールドを通じて悪意のあるSQLコードを挿入し、データベースに不正なクエリを実行させます。これにより、データの漏洩、改ざん、削除などの被害が生じる可能性があります。防止策としては、パラメータ化クエリの使用、入力のバリデーション、最小権限の原則などがあります。", 61 | "feedback": "正確: SQLインジェクションの本質と発生メカニズムをよく説明しています", 62 | "is_correct": 1.0, 63 | "response_time": 1.6 64 | }, 65 | { 66 | "question": "NFTとは何ですか?", 67 | "answer": "NFT(Non-Fungible Token)は、代替不可能なトークンで、デジタルアセットの所有権を証明するためのブロックチェーン技術です。デジタルアート、コレクティブル、音楽などに利用されています。", 68 | "correct_answer": "NFT(Non-Fungible Token、非代替性トークン)はブロックチェーン上に記録された固有の識別子を持つデジタル資産です。通常の暗号通貨と異なり、各NFTは独自の価値を持ち、交換不可能です。デジタルアート、音楽、ゲーム内アイテム、バーチャル不動産など様々なデジタル資産の所有権証明や取引に利用されています。", 69 | "feedback": "正確: NFTの基本概念とユースケースを明確に説明しています", 70 | "is_correct": 1.0, 71 | "response_time": 1.4 72 | }, 73 | { 74 | "question": "Pythonのデコレータとは何ですか?", 75 | "answer": "デコレータは、関数やメソッドを修飾するための構文で、@記号を使用します。関数の機能を変更したり拡張したりするための便利な方法です。", 76 | "correct_answer": "Pythonのデコレータは、既存の関数やメソッドを修飾して機能を拡張するための構文です。@記号を使用して関数定義の前に配置します。デコレータは高階関数で、別の関数を引数として受け取り、新しい関数を返します。ロギング、認証、キャッシングなど、コードの重複を避けながら横断的関心事を実装するのに役立ちます。", 77 | "feedback": "部分的に正確: 基本的な説明はありますが、デコレータが高階関数であることや具体的な使用例の説明が不足しています", 78 | "is_correct": 0.5, 79 | "response_time": 1.2 80 | }, 81 | { 82 | "question": "コンテナ技術とは何ですか?", 83 | "answer": "コンテナ技術は、アプリケーションとその依存関係をパッケージ化し、異なる環境で一貫して実行できるようにする軽量な仮想化技術です。", 84 | "correct_answer": "コンテナ技術は、アプリケーションとその依存関係(ライブラリ、バイナリなど)を一つのパッケージにカプセル化する軽量な仮想化技術です。コンテナは仮想マシンよりも軽量で起動が速く、ホストOSのカーネルを共有します。Dockerが代表的なコンテナプラットフォームで、アプリケーションの開発、テスト、デプロイメントを効率化し、「どこでも同じように動作する」環境を提供します。", 85 | "feedback": "部分的に正確: 基本的な説明はありますが、仮想マシンとの違いやDockerなどの具体例の説明が不足しています", 86 | "is_correct": 0.5, 87 | "response_time": 1.1 88 | } 89 | ] 90 | 91 | 92 | def create_sample_evaluation_data(): 93 | """定義されたサンプルデータをデータベースに保存する""" 94 | try: 95 | count_before = get_db_count() 96 | added_count = 0 97 | # 各サンプルをデータベースに保存 98 | for item in SAMPLE_QUESTIONS_DATA: 99 | # save_to_dbが必要な引数のみ渡す 100 | save_to_db( 101 | question=item["question"], 102 | answer=item["answer"], 103 | feedback=item["feedback"], 104 | correct_answer=item["correct_answer"], 105 | is_correct=item["is_correct"], 106 | response_time=item["response_time"] 107 | ) 108 | added_count += 1 109 | 110 | count_after = get_db_count() 111 | st.success(f"{added_count} 件のサンプル評価データが正常に追加されました。(合計: {count_after} 件)") 112 | 113 | except Exception as e: 114 | st.error(f"サンプルデータの作成中にエラーが発生しました: {e}") 115 | print(f"エラー詳細: {e}") # コンソールにも出力 116 | 117 | def ensure_initial_data(): 118 | """データベースが空の場合に初期サンプルデータを投入する""" 119 | if get_db_count() == 0: 120 | st.info("データベースが空です。初期サンプルデータを投入します。") 121 | create_sample_evaluation_data() -------------------------------------------------------------------------------- /day1/02_streamlit_app/database.py: -------------------------------------------------------------------------------- 1 | # database.py 2 | import sqlite3 3 | import pandas as pd 4 | from datetime import datetime 5 | import streamlit as st 6 | from config import DB_FILE 7 | from metrics import calculate_metrics # metricsを計算するために必要 8 | 9 | # --- スキーマ定義 --- 10 | TABLE_NAME = "chat_history" 11 | SCHEMA = f''' 12 | CREATE TABLE IF NOT EXISTS {TABLE_NAME} 13 | (id INTEGER PRIMARY KEY AUTOINCREMENT, 14 | timestamp TEXT, 15 | question TEXT, 16 | answer TEXT, 17 | feedback TEXT, 18 | correct_answer TEXT, 19 | is_correct REAL, -- INTEGERからREALに変更 (0.5を許容するため) 20 | response_time REAL, 21 | bleu_score REAL, 22 | similarity_score REAL, 23 | word_count INTEGER, 24 | relevance_score REAL) 25 | ''' 26 | 27 | # --- データベース初期化 --- 28 | def init_db(): 29 | """データベースとテーブルを初期化する""" 30 | try: 31 | conn = sqlite3.connect(DB_FILE) 32 | c = conn.cursor() 33 | c.execute(SCHEMA) 34 | conn.commit() 35 | conn.close() 36 | print(f"Database '{DB_FILE}' initialized successfully.") 37 | except Exception as e: 38 | st.error(f"データベースの初期化に失敗しました: {e}") 39 | raise e # エラーを再発生させてアプリの起動を止めるか、適切に処理する 40 | 41 | # --- データ操作関数 --- 42 | def save_to_db(question, answer, feedback, correct_answer, is_correct, response_time): 43 | """チャット履歴と評価指標をデータベースに保存する""" 44 | conn = None 45 | try: 46 | conn = sqlite3.connect(DB_FILE) 47 | c = conn.cursor() 48 | timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") 49 | 50 | # 追加の評価指標を計算 51 | bleu_score, similarity_score, word_count, relevance_score = calculate_metrics( 52 | answer, correct_answer 53 | ) 54 | 55 | c.execute(f''' 56 | INSERT INTO {TABLE_NAME} (timestamp, question, answer, feedback, correct_answer, is_correct, 57 | response_time, bleu_score, similarity_score, word_count, relevance_score) 58 | VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) 59 | ''', (timestamp, question, answer, feedback, correct_answer, is_correct, 60 | response_time, bleu_score, similarity_score, word_count, relevance_score)) 61 | conn.commit() 62 | print("Data saved to DB successfully.") # デバッグ用 63 | except sqlite3.Error as e: 64 | st.error(f"データベースへの保存中にエラーが発生しました: {e}") 65 | finally: 66 | if conn: 67 | conn.close() 68 | 69 | def get_chat_history(): 70 | """データベースから全てのチャット履歴を取得する""" 71 | conn = None 72 | try: 73 | conn = sqlite3.connect(DB_FILE) 74 | # is_correctがREAL型なので、それに応じて読み込む 75 | df = pd.read_sql_query(f"SELECT * FROM {TABLE_NAME} ORDER BY timestamp DESC", conn) 76 | # is_correct カラムのデータ型を確認し、必要なら変換 77 | if 'is_correct' in df.columns: 78 | df['is_correct'] = pd.to_numeric(df['is_correct'], errors='coerce') # 数値に変換、失敗したらNaN 79 | return df 80 | except sqlite3.Error as e: 81 | st.error(f"履歴の取得中にエラーが発生しました: {e}") 82 | return pd.DataFrame() # 空のDataFrameを返す 83 | finally: 84 | if conn: 85 | conn.close() 86 | 87 | def get_db_count(): 88 | """データベース内のレコード数を取得する""" 89 | conn = None 90 | try: 91 | conn = sqlite3.connect(DB_FILE) 92 | c = conn.cursor() 93 | c.execute(f"SELECT COUNT(*) FROM {TABLE_NAME}") 94 | count = c.fetchone()[0] 95 | return count 96 | except sqlite3.Error as e: 97 | st.error(f"レコード数の取得中にエラーが発生しました: {e}") 98 | return 0 99 | finally: 100 | if conn: 101 | conn.close() 102 | 103 | def clear_db(): 104 | """データベースの全レコードを削除する""" 105 | conn = None 106 | confirmed = st.session_state.get("confirm_clear", False) 107 | 108 | if not confirmed: 109 | st.warning("本当にすべてのデータを削除しますか?もう一度「データベースをクリア」ボタンを押すと削除が実行されます。") 110 | st.session_state.confirm_clear = True 111 | return False # 削除は実行されなかった 112 | 113 | try: 114 | conn = sqlite3.connect(DB_FILE) 115 | c = conn.cursor() 116 | c.execute(f"DELETE FROM {TABLE_NAME}") 117 | conn.commit() 118 | st.success("データベースが正常にクリアされました。") 119 | st.session_state.confirm_clear = False # 確認状態をリセット 120 | return True # 削除成功 121 | except sqlite3.Error as e: 122 | st.error(f"データベースのクリア中にエラーが発生しました: {e}") 123 | st.session_state.confirm_clear = False # エラー時もリセット 124 | return False # 削除失敗 125 | finally: 126 | if conn: 127 | conn.close() -------------------------------------------------------------------------------- /day1/02_streamlit_app/llm.py: -------------------------------------------------------------------------------- 1 | # llm.py 2 | import os 3 | import torch 4 | from transformers import pipeline 5 | import streamlit as st 6 | import time 7 | from config import MODEL_NAME 8 | from huggingface_hub import login 9 | 10 | # モデルをキャッシュして再利用 11 | @st.cache_resource 12 | def load_model(): 13 | """LLMモデルをロードする""" 14 | try: 15 | 16 | # アクセストークンを保存 17 | hf_token = st.secrets["huggingface"]["token"] 18 | 19 | device = "cuda" if torch.cuda.is_available() else "cpu" 20 | st.info(f"Using device: {device}") # 使用デバイスを表示 21 | pipe = pipeline( 22 | "text-generation", 23 | model=MODEL_NAME, 24 | model_kwargs={"torch_dtype": torch.bfloat16}, 25 | device=device 26 | ) 27 | st.success(f"モデル '{MODEL_NAME}' の読み込みに成功しました。") 28 | return pipe 29 | except Exception as e: 30 | st.error(f"モデル '{MODEL_NAME}' の読み込みに失敗しました: {e}") 31 | st.error("GPUメモリ不足の可能性があります。不要なプロセスを終了するか、より小さいモデルの使用を検討してください。") 32 | return None 33 | 34 | def generate_response(pipe, user_question): 35 | """LLMを使用して質問に対する回答を生成する""" 36 | if pipe is None: 37 | return "モデルがロードされていないため、回答を生成できません。", 0 38 | 39 | try: 40 | start_time = time.time() 41 | messages = [ 42 | {"role": "user", "content": user_question}, 43 | ] 44 | # max_new_tokensを調整可能にする(例) 45 | outputs = pipe(messages, max_new_tokens=512, do_sample=True, temperature=0.7, top_p=0.9) 46 | 47 | # Gemmaの出力形式に合わせて調整が必要な場合がある 48 | # 最後のassistantのメッセージを取得 49 | assistant_response = "" 50 | if outputs and isinstance(outputs, list) and outputs[0].get("generated_text"): 51 | if isinstance(outputs[0]["generated_text"], list) and len(outputs[0]["generated_text"]) > 0: 52 | # messages形式の場合 53 | last_message = outputs[0]["generated_text"][-1] 54 | if last_message.get("role") == "assistant": 55 | assistant_response = last_message.get("content", "").strip() 56 | elif isinstance(outputs[0]["generated_text"], str): 57 | # 単純な文字列の場合(古いtransformers?) - プロンプト部分を除く処理が必要かも 58 | # この部分はモデルやtransformersのバージョンによって調整が必要 59 | full_text = outputs[0]["generated_text"] 60 | # 簡単な方法:ユーザーの質問以降の部分を取得 61 | prompt_end = user_question 62 | response_start_index = full_text.find(prompt_end) + len(prompt_end) 63 | # 応答部分のみを抽出(より堅牢な方法が必要な場合あり) 64 | possible_response = full_text[response_start_index:].strip() 65 | # 特定の開始トークンを探すなど、モデルに合わせた調整 66 | if "model" in possible_response: 67 | assistant_response = possible_response.split("model\n")[-1].strip() 68 | else: 69 | assistant_response = possible_response # フォールバック 70 | 71 | if not assistant_response: 72 | # 上記で見つからない場合のフォールバックやデバッグ 73 | print("Warning: Could not extract assistant response. Full output:", outputs) 74 | assistant_response = "回答の抽出に失敗しました。" 75 | 76 | 77 | end_time = time.time() 78 | response_time = end_time - start_time 79 | print(f"Generated response in {response_time:.2f}s") # デバッグ用 80 | return assistant_response, response_time 81 | 82 | except Exception as e: 83 | st.error(f"回答生成中にエラーが発生しました: {e}") 84 | # エラーの詳細をログに出力 85 | import traceback 86 | traceback.print_exc() 87 | return f"エラーが発生しました: {str(e)}", 0 -------------------------------------------------------------------------------- /day1/02_streamlit_app/metrics.py: -------------------------------------------------------------------------------- 1 | # metrics.py 2 | import streamlit as st 3 | import nltk 4 | from janome.tokenizer import Tokenizer 5 | import re 6 | from sklearn.metrics.pairwise import cosine_similarity 7 | from sklearn.feature_extraction.text import TfidfVectorizer 8 | 9 | # NLTKのヘルパー関数(エラー時フォールバック付き) 10 | try: 11 | nltk.download('punkt', quiet=True) 12 | from nltk.translate.bleu_score import sentence_bleu as nltk_sentence_bleu 13 | from nltk.tokenize import word_tokenize as nltk_word_tokenize 14 | print("NLTK loaded successfully.") # デバッグ用 15 | except Exception as e: 16 | st.warning(f"NLTKの初期化中にエラーが発生しました: {e}\n簡易的な代替関数を使用します。") 17 | def nltk_word_tokenize(text): 18 | return text.split() 19 | def nltk_sentence_bleu(references, candidate): 20 | # 簡易BLEUスコア(完全一致/部分一致) 21 | ref_words = set(references[0]) 22 | cand_words = set(candidate) 23 | common_words = ref_words.intersection(cand_words) 24 | precision = len(common_words) / len(cand_words) if cand_words else 0 25 | recall = len(common_words) / len(ref_words) if ref_words else 0 26 | f1 = 2 * (precision * recall) / (precision + recall) if (precision + recall) > 0 else 0 27 | return f1 # F1スコアを返す(簡易的な代替) 28 | 29 | def initialize_nltk(): 30 | """NLTKのデータダウンロードを試みる関数""" 31 | try: 32 | nltk.download('punkt', quiet=True) 33 | print("NLTK Punkt data checked/downloaded.") # デバッグ用 34 | except Exception as e: 35 | st.error(f"NLTKデータのダウンロードに失敗しました: {e}") 36 | 37 | def calculate_metrics(answer, correct_answer): 38 | """回答と正解から評価指標を計算する""" 39 | word_count = 0 40 | bleu_score = 0.0 41 | similarity_score = 0.0 42 | relevance_score = 0.0 43 | 44 | if not answer: # 回答がない場合は計算しない 45 | return bleu_score, similarity_score, word_count, relevance_score 46 | 47 | # 単語数のカウント 48 | tokenizer = Tokenizer() 49 | tokens = list(tokenizer.tokenize(answer)) # ← list() でイテレータをリストに変換 50 | word_count = len(tokens) 51 | 52 | # 正解がある場合のみBLEUと類似度を計算 53 | if correct_answer: 54 | answer_lower = answer.lower() 55 | correct_answer_lower = correct_answer.lower() 56 | 57 | # BLEU スコアの計算 58 | try: 59 | reference = [nltk_word_tokenize(correct_answer_lower)] 60 | candidate = nltk_word_tokenize(answer_lower) 61 | # ゼロ除算エラーを防ぐ 62 | if candidate: 63 | bleu_score = nltk_sentence_bleu(reference, candidate, weights=(0.25, 0.25, 0.25, 0.25)) # 4-gram BLEU 64 | else: 65 | bleu_score = 0.0 66 | except Exception as e: 67 | # st.warning(f"BLEUスコア計算エラー: {e}") 68 | bleu_score = 0.0 # エラー時は0 69 | 70 | # コサイン類似度の計算 71 | try: 72 | vectorizer = TfidfVectorizer() 73 | # fit_transformはリストを期待するため、リストで渡す 74 | if answer_lower.strip() and correct_answer_lower.strip(): # 空文字列でないことを確認 75 | tfidf_matrix = vectorizer.fit_transform([answer_lower, correct_answer_lower]) 76 | similarity_score = cosine_similarity(tfidf_matrix[0:1], tfidf_matrix[1:2])[0][0] 77 | else: 78 | similarity_score = 0.0 79 | except Exception as e: 80 | # st.warning(f"類似度スコア計算エラー: {e}") 81 | similarity_score = 0.0 # エラー時は0 82 | 83 | # 関連性スコア(キーワードの一致率などで簡易的に計算) 84 | try: 85 | answer_words = set(re.findall(r'\w+', answer_lower)) 86 | correct_words = set(re.findall(r'\w+', correct_answer_lower)) 87 | if len(correct_words) > 0: 88 | common_words = answer_words.intersection(correct_words) 89 | relevance_score = len(common_words) / len(correct_words) 90 | else: 91 | relevance_score = 0.0 92 | except Exception as e: 93 | # st.warning(f"関連性スコア計算エラー: {e}") 94 | relevance_score = 0.0 # エラー時は0 95 | 96 | return bleu_score, similarity_score, word_count, relevance_score 97 | 98 | def get_metrics_descriptions(): 99 | """評価指標の説明を返す""" 100 | return { 101 | "正確性スコア (is_correct)": "回答の正確さを3段階で評価: 1.0 (正確), 0.5 (部分的に正確), 0.0 (不正確)", 102 | "応答時間 (response_time)": "質問を投げてから回答を得るまでの時間(秒)。モデルの効率性を表す", 103 | "BLEU スコア (bleu_score)": "機械翻訳評価指標で、正解と回答のn-gramの一致度を測定 (0〜1の値、高いほど類似)", 104 | "類似度スコア (similarity_score)": "TF-IDFベクトルのコサイン類似度による、正解と回答の意味的な類似性 (0〜1の値)", 105 | "単語数 (word_count)": "回答に含まれる単語の数。情報量や詳細さの指標", 106 | "関連性スコア (relevance_score)": "正解と回答の共通単語の割合。トピックの関連性を表す (0〜1の値)", 107 | "効率性スコア (efficiency_score)": "正確性を応答時間で割った値。高速で正確な回答ほど高スコア" 108 | } -------------------------------------------------------------------------------- /day1/02_streamlit_app/requirements.txt: -------------------------------------------------------------------------------- 1 | streamlit 2 | torch 3 | transformers 4 | pandas 5 | nltk 6 | scikit-learn 7 | accelerate 8 | janome 9 | pyngrok -------------------------------------------------------------------------------- /day1/02_streamlit_app/ui.py: -------------------------------------------------------------------------------- 1 | # ui.py 2 | import streamlit as st 3 | import pandas as pd 4 | import time 5 | from database import save_to_db, get_chat_history, get_db_count, clear_db 6 | from llm import generate_response 7 | from data import create_sample_evaluation_data 8 | from metrics import get_metrics_descriptions 9 | 10 | # --- チャットページのUI --- 11 | def display_chat_page(pipe): 12 | """チャットページのUIを表示する""" 13 | st.subheader("質問を入力してください") 14 | user_question = st.text_area("質問", key="question_input", height=100, value=st.session_state.get("current_question", "")) 15 | submit_button = st.button("質問を送信") 16 | 17 | # セッション状態の初期化(安全のため) 18 | if "current_question" not in st.session_state: 19 | st.session_state.current_question = "" 20 | if "current_answer" not in st.session_state: 21 | st.session_state.current_answer = "" 22 | if "response_time" not in st.session_state: 23 | st.session_state.response_time = 0.0 24 | if "feedback_given" not in st.session_state: 25 | st.session_state.feedback_given = False 26 | 27 | # 質問が送信された場合 28 | if submit_button and user_question: 29 | st.session_state.current_question = user_question 30 | st.session_state.current_answer = "" # 回答をリセット 31 | st.session_state.feedback_given = False # フィードバック状態もリセット 32 | 33 | with st.spinner("モデルが回答を生成中..."): 34 | answer, response_time = generate_response(pipe, user_question) 35 | st.session_state.current_answer = answer 36 | st.session_state.response_time = response_time 37 | # ここでrerunすると回答とフィードバックが一度に表示される 38 | st.rerun() 39 | 40 | # 回答が表示されるべきか判断 (質問があり、回答が生成済みで、まだフィードバックされていない) 41 | if st.session_state.current_question and st.session_state.current_answer: 42 | st.subheader("回答:") 43 | st.markdown(st.session_state.current_answer) # Markdownで表示 44 | st.info(f"応答時間: {st.session_state.response_time:.2f}秒") 45 | 46 | # フィードバックフォームを表示 (まだフィードバックされていない場合) 47 | if not st.session_state.feedback_given: 48 | display_feedback_form() 49 | else: 50 | # フィードバック送信済みの場合、次の質問を促すか、リセットする 51 | if st.button("次の質問へ"): 52 | # 状態をリセット 53 | st.session_state.current_question = "" 54 | st.session_state.current_answer = "" 55 | st.session_state.response_time = 0.0 56 | st.session_state.feedback_given = False 57 | st.rerun() # 画面をクリア 58 | 59 | 60 | def display_feedback_form(): 61 | """フィードバック入力フォームを表示する""" 62 | with st.form("feedback_form"): 63 | st.subheader("フィードバック") 64 | feedback_options = ["正確", "部分的に正確", "不正確"] 65 | # label_visibility='collapsed' でラベルを隠す 66 | feedback = st.radio("回答の評価", feedback_options, key="feedback_radio", label_visibility='collapsed', horizontal=True) 67 | correct_answer = st.text_area("より正確な回答(任意)", key="correct_answer_input", height=100) 68 | feedback_comment = st.text_area("コメント(任意)", key="feedback_comment_input", height=100) 69 | submitted = st.form_submit_button("フィードバックを送信") 70 | if submitted: 71 | # フィードバックをデータベースに保存 72 | is_correct = 1.0 if feedback == "正確" else (0.5 if feedback == "部分的に正確" else 0.0) 73 | # コメントがない場合でも '正確' などの評価はfeedbackに含まれるようにする 74 | combined_feedback = f"{feedback}" 75 | if feedback_comment: 76 | combined_feedback += f": {feedback_comment}" 77 | 78 | save_to_db( 79 | st.session_state.current_question, 80 | st.session_state.current_answer, 81 | combined_feedback, 82 | correct_answer, 83 | is_correct, 84 | st.session_state.response_time 85 | ) 86 | st.session_state.feedback_given = True 87 | st.success("フィードバックが保存されました!") 88 | # フォーム送信後に状態をリセットしない方が、ユーザーは結果を確認しやすいかも 89 | # 必要ならここでリセットして st.rerun() 90 | st.rerun() # フィードバックフォームを消すために再実行 91 | 92 | # --- 履歴閲覧ページのUI --- 93 | def display_history_page(): 94 | """履歴閲覧ページのUIを表示する""" 95 | st.subheader("チャット履歴と評価指標") 96 | history_df = get_chat_history() 97 | 98 | if history_df.empty: 99 | st.info("まだチャット履歴がありません。") 100 | return 101 | 102 | # タブでセクションを分ける 103 | tab1, tab2 = st.tabs(["履歴閲覧", "評価指標分析"]) 104 | 105 | with tab1: 106 | display_history_list(history_df) 107 | 108 | with tab2: 109 | display_metrics_analysis(history_df) 110 | 111 | def display_history_list(history_df): 112 | """履歴リストを表示する""" 113 | st.write("#### 履歴リスト") 114 | # 表示オプション 115 | filter_options = { 116 | "すべて表示": None, 117 | "正確なもののみ": 1.0, 118 | "部分的に正確なもののみ": 0.5, 119 | "不正確なもののみ": 0.0 120 | } 121 | display_option = st.radio( 122 | "表示フィルタ", 123 | options=filter_options.keys(), 124 | horizontal=True, 125 | label_visibility="collapsed" # ラベル非表示 126 | ) 127 | 128 | filter_value = filter_options[display_option] 129 | if filter_value is not None: 130 | # is_correctがNaNの場合を考慮 131 | filtered_df = history_df[history_df["is_correct"].notna() & (history_df["is_correct"] == filter_value)] 132 | else: 133 | filtered_df = history_df 134 | 135 | if filtered_df.empty: 136 | st.info("選択した条件に一致する履歴はありません。") 137 | return 138 | 139 | # ページネーション 140 | items_per_page = 5 141 | total_items = len(filtered_df) 142 | total_pages = (total_items + items_per_page - 1) // items_per_page 143 | current_page = st.number_input('ページ', min_value=1, max_value=total_pages, value=1, step=1) 144 | 145 | start_idx = (current_page - 1) * items_per_page 146 | end_idx = start_idx + items_per_page 147 | paginated_df = filtered_df.iloc[start_idx:end_idx] 148 | 149 | 150 | for i, row in paginated_df.iterrows(): 151 | with st.expander(f"{row['timestamp']} - Q: {row['question'][:50] if row['question'] else 'N/A'}..."): 152 | st.markdown(f"**Q:** {row['question']}") 153 | st.markdown(f"**A:** {row['answer']}") 154 | st.markdown(f"**Feedback:** {row['feedback']}") 155 | if row['correct_answer']: 156 | st.markdown(f"**Correct A:** {row['correct_answer']}") 157 | 158 | # 評価指標の表示 159 | st.markdown("---") 160 | cols = st.columns(3) 161 | cols[0].metric("正確性スコア", f"{row['is_correct']:.1f}") 162 | cols[1].metric("応答時間(秒)", f"{row['response_time']:.2f}") 163 | cols[2].metric("単語数", f"{row['word_count']}") 164 | 165 | cols = st.columns(3) 166 | # NaNの場合はハイフン表示 167 | cols[0].metric("BLEU", f"{row['bleu_score']:.4f}" if pd.notna(row['bleu_score']) else "-") 168 | cols[1].metric("類似度", f"{row['similarity_score']:.4f}" if pd.notna(row['similarity_score']) else "-") 169 | cols[2].metric("関連性", f"{row['relevance_score']:.4f}" if pd.notna(row['relevance_score']) else "-") 170 | 171 | st.caption(f"{total_items} 件中 {start_idx+1} - {min(end_idx, total_items)} 件を表示") 172 | 173 | 174 | def display_metrics_analysis(history_df): 175 | """評価指標の分析結果を表示する""" 176 | st.write("#### 評価指標の分析") 177 | 178 | # is_correct が NaN のレコードを除外して分析 179 | analysis_df = history_df.dropna(subset=['is_correct']) 180 | if analysis_df.empty: 181 | st.warning("分析可能な評価データがありません。") 182 | return 183 | 184 | accuracy_labels = {1.0: '正確', 0.5: '部分的に正確', 0.0: '不正確'} 185 | analysis_df['正確性'] = analysis_df['is_correct'].map(accuracy_labels) 186 | 187 | # 正確性の分布 188 | st.write("##### 正確性の分布") 189 | accuracy_counts = analysis_df['正確性'].value_counts() 190 | if not accuracy_counts.empty: 191 | st.bar_chart(accuracy_counts) 192 | else: 193 | st.info("正確性データがありません。") 194 | 195 | # 応答時間と他の指標の関係 196 | st.write("##### 応答時間とその他の指標の関係") 197 | metric_options = ["bleu_score", "similarity_score", "relevance_score", "word_count"] 198 | # 利用可能な指標のみ選択肢に含める 199 | valid_metric_options = [m for m in metric_options if m in analysis_df.columns and analysis_df[m].notna().any()] 200 | 201 | if valid_metric_options: 202 | metric_option = st.selectbox( 203 | "比較する評価指標を選択", 204 | valid_metric_options, 205 | key="metric_select" 206 | ) 207 | 208 | chart_data = analysis_df[['response_time', metric_option, '正確性']].dropna() # NaNを除外 209 | if not chart_data.empty: 210 | st.scatter_chart( 211 | chart_data, 212 | x='response_time', 213 | y=metric_option, 214 | color='正確性', 215 | ) 216 | else: 217 | st.info(f"選択された指標 ({metric_option}) と応答時間の有効なデータがありません。") 218 | 219 | else: 220 | st.info("応答時間と比較できる指標データがありません。") 221 | 222 | 223 | # 全体の評価指標の統計 224 | st.write("##### 評価指標の統計") 225 | stats_cols = ['response_time', 'bleu_score', 'similarity_score', 'word_count', 'relevance_score'] 226 | valid_stats_cols = [c for c in stats_cols if c in analysis_df.columns and analysis_df[c].notna().any()] 227 | if valid_stats_cols: 228 | metrics_stats = analysis_df[valid_stats_cols].describe() 229 | st.dataframe(metrics_stats) 230 | else: 231 | st.info("統計情報を計算できる評価指標データがありません。") 232 | 233 | # 正確性レベル別の平均スコア 234 | st.write("##### 正確性レベル別の平均スコア") 235 | if valid_stats_cols and '正確性' in analysis_df.columns: 236 | try: 237 | accuracy_groups = analysis_df.groupby('正確性')[valid_stats_cols].mean() 238 | st.dataframe(accuracy_groups) 239 | except Exception as e: 240 | st.warning(f"正確性別スコアの集計中にエラーが発生しました: {e}") 241 | else: 242 | st.info("正確性レベル別の平均スコアを計算できるデータがありません。") 243 | 244 | 245 | # カスタム評価指標:効率性スコア 246 | st.write("##### 効率性スコア (正確性 / (応答時間 + 0.1))") 247 | if 'response_time' in analysis_df.columns and analysis_df['response_time'].notna().any(): 248 | # ゼロ除算を避けるために0.1を追加 249 | analysis_df['efficiency_score'] = analysis_df['is_correct'] / (analysis_df['response_time'].fillna(0) + 0.1) 250 | # IDカラムが存在するか確認 251 | if 'id' in analysis_df.columns: 252 | # 上位10件を表示 253 | top_efficiency = analysis_df.sort_values('efficiency_score', ascending=False).head(10) 254 | # id をインデックスにする前に存在確認 255 | if not top_efficiency.empty: 256 | st.bar_chart(top_efficiency.set_index('id')['efficiency_score']) 257 | else: 258 | st.info("効率性スコアデータがありません。") 259 | else: 260 | # IDがない場合は単純にスコアを表示 261 | st.bar_chart(analysis_df.sort_values('efficiency_score', ascending=False).head(10)['efficiency_score']) 262 | 263 | else: 264 | st.info("効率性スコアを計算するための応答時間データがありません。") 265 | 266 | 267 | # --- サンプルデータ管理ページのUI --- 268 | def display_data_page(): 269 | """サンプルデータ管理ページのUIを表示する""" 270 | st.subheader("サンプル評価データの管理") 271 | count = get_db_count() 272 | st.write(f"現在のデータベースには {count} 件のレコードがあります。") 273 | 274 | col1, col2 = st.columns(2) 275 | with col1: 276 | if st.button("サンプルデータを追加", key="create_samples"): 277 | create_sample_evaluation_data() 278 | st.rerun() # 件数表示を更新 279 | 280 | with col2: 281 | # 確認ステップ付きのクリアボタン 282 | if st.button("データベースをクリア", key="clear_db_button"): 283 | if clear_db(): # clear_db内で確認と実行を行う 284 | st.rerun() # クリア後に件数表示を更新 285 | 286 | # 評価指標に関する解説 287 | st.subheader("評価指標の説明") 288 | metrics_info = get_metrics_descriptions() 289 | for metric, description in metrics_info.items(): 290 | with st.expander(f"{metric}"): 291 | st.write(description) -------------------------------------------------------------------------------- /day1/03_FastAPI/app.py: -------------------------------------------------------------------------------- 1 | import os 2 | import torch 3 | from transformers import pipeline 4 | import time 5 | import traceback 6 | from fastapi import FastAPI, HTTPException, BackgroundTasks 7 | from fastapi.middleware.cors import CORSMiddleware 8 | from pydantic import BaseModel 9 | from typing import Optional, List, Dict, Any 10 | import uvicorn 11 | import nest_asyncio 12 | from pyngrok import ngrok 13 | 14 | # --- 設定 --- 15 | # モデル名を設定 16 | MODEL_NAME = "google/gemma-2-2b-jpn-it" # お好みのモデルに変更可能です 17 | print(f"モデル名を設定: {MODEL_NAME}") 18 | 19 | # --- モデル設定クラス --- 20 | class Config: 21 | def __init__(self, model_name=MODEL_NAME): 22 | self.MODEL_NAME = model_name 23 | 24 | config = Config(MODEL_NAME) 25 | 26 | # --- FastAPIアプリケーション定義 --- 27 | app = FastAPI( 28 | title="ローカルLLM APIサービス", 29 | description="transformersモデルを使用したテキスト生成のためのAPI", 30 | version="1.0.0" 31 | ) 32 | 33 | # CORSミドルウェアを追加 34 | app.add_middleware( 35 | CORSMiddleware, 36 | allow_origins=["*"], 37 | allow_credentials=True, 38 | allow_methods=["*"], 39 | allow_headers=["*"], 40 | ) 41 | 42 | # --- データモデル定義 --- 43 | class Message(BaseModel): 44 | role: str 45 | content: str 46 | 47 | # 直接プロンプトを使用した簡略化されたリクエスト 48 | class SimpleGenerationRequest(BaseModel): 49 | prompt: str 50 | max_new_tokens: Optional[int] = 512 51 | do_sample: Optional[bool] = True 52 | temperature: Optional[float] = 0.7 53 | top_p: Optional[float] = 0.9 54 | 55 | class GenerationResponse(BaseModel): 56 | generated_text: str 57 | response_time: float 58 | 59 | # --- モデル関連の関数 --- 60 | # モデルのグローバル変数 61 | model = None 62 | 63 | def load_model(): 64 | """推論用のLLMモデルを読み込む""" 65 | global model # グローバル変数を更新するために必要 66 | try: 67 | device = "cuda" if torch.cuda.is_available() else "cpu" 68 | print(f"使用デバイス: {device}") 69 | pipe = pipeline( 70 | "text-generation", 71 | model=config.MODEL_NAME, 72 | model_kwargs={"torch_dtype": torch.bfloat16}, 73 | device=device 74 | ) 75 | print(f"モデル '{config.MODEL_NAME}' の読み込みに成功しました") 76 | model = pipe # グローバル変数を更新 77 | return pipe 78 | except Exception as e: 79 | error_msg = f"モデル '{config.MODEL_NAME}' の読み込みに失敗: {e}" 80 | print(error_msg) 81 | traceback.print_exc() # 詳細なエラー情報を出力 82 | return None 83 | 84 | def extract_assistant_response(outputs, user_prompt): 85 | """モデルの出力からアシスタントの応答を抽出する""" 86 | assistant_response = "" 87 | try: 88 | if outputs and isinstance(outputs, list) and len(outputs) > 0 and outputs[0].get("generated_text"): 89 | generated_output = outputs[0]["generated_text"] 90 | 91 | if isinstance(generated_output, list): 92 | # メッセージフォーマットの場合 93 | if len(generated_output) > 0: 94 | last_message = generated_output[-1] 95 | if isinstance(last_message, dict) and last_message.get("role") == "assistant": 96 | assistant_response = last_message.get("content", "").strip() 97 | else: 98 | # 予期しないリスト形式の場合は最後の要素を文字列として試行 99 | print(f"警告: 最後のメッセージの形式が予期しないリスト形式です: {last_message}") 100 | assistant_response = str(last_message).strip() 101 | 102 | elif isinstance(generated_output, str): 103 | # 文字列形式の場合 104 | full_text = generated_output 105 | 106 | # 単純なプロンプト入力の場合、プロンプト後の全てを抽出 107 | if user_prompt: 108 | prompt_end_index = full_text.find(user_prompt) 109 | if prompt_end_index != -1: 110 | prompt_end_pos = prompt_end_index + len(user_prompt) 111 | assistant_response = full_text[prompt_end_pos:].strip() 112 | else: 113 | # 元のプロンプトが見つからない場合は、生成されたテキストをそのまま返す 114 | assistant_response = full_text 115 | else: 116 | assistant_response = full_text 117 | else: 118 | print(f"警告: 予期しない出力タイプ: {type(generated_output)}") 119 | assistant_response = str(generated_output).strip() # 文字列に変換 120 | 121 | except Exception as e: 122 | print(f"応答の抽出中にエラーが発生しました: {e}") 123 | traceback.print_exc() 124 | assistant_response = "応答の抽出に失敗しました。" # エラーメッセージを設定 125 | 126 | if not assistant_response: 127 | print("警告: アシスタントの応答を抽出できませんでした。完全な出力:", outputs) 128 | # デフォルトまたはエラー応答を返す 129 | assistant_response = "応答を生成できませんでした。" 130 | 131 | return assistant_response 132 | 133 | # --- FastAPIエンドポイント定義 --- 134 | @app.on_event("startup") 135 | async def startup_event(): 136 | """起動時にモデルを初期化""" 137 | load_model_task() # バックグラウンドではなく同期的に読み込む 138 | if model is None: 139 | print("警告: 起動時にモデルの初期化に失敗しました") 140 | else: 141 | print("起動時にモデルの初期化が完了しました。") 142 | 143 | @app.get("/") 144 | async def root(): 145 | """基本的なAPIチェック用のルートエンドポイント""" 146 | return {"status": "ok", "message": "Local LLM API is runnning"} 147 | 148 | @app.get("/health") 149 | async def health_check(): 150 | """ヘルスチェックエンドポイント""" 151 | global model 152 | if model is None: 153 | return {"status": "error", "message": "No model loaded"} 154 | 155 | return {"status": "ok", "model": config.MODEL_NAME} 156 | 157 | # 簡略化されたエンドポイント 158 | @app.post("/generate", response_model=GenerationResponse) 159 | async def generate_simple(request: SimpleGenerationRequest): 160 | """単純なプロンプト入力に基づいてテキストを生成""" 161 | global model 162 | 163 | if model is None: 164 | print("generateエンドポイント: モデルが読み込まれていません。読み込みを試みます...") 165 | load_model_task() # 再度読み込みを試みる 166 | if model is None: 167 | print("generateエンドポイント: モデルの読み込みに失敗しました。") 168 | raise HTTPException(status_code=503, detail="モデルが利用できません。後でもう一度お試しください。") 169 | 170 | try: 171 | start_time = time.time() 172 | print(f"シンプルなリクエストを受信: prompt={request.prompt[:100]}..., max_new_tokens={request.max_new_tokens}") # 長いプロンプトは切り捨て 173 | 174 | # プロンプトテキストで直接応答を生成 175 | print("モデル推論を開始...") 176 | outputs = model( 177 | request.prompt, 178 | max_new_tokens=request.max_new_tokens, 179 | do_sample=request.do_sample, 180 | temperature=request.temperature, 181 | top_p=request.top_p, 182 | ) 183 | print("モデル推論が完了しました。") 184 | 185 | # アシスタント応答を抽出 186 | assistant_response = extract_assistant_response(outputs, request.prompt) 187 | print(f"抽出されたアシスタント応答: {assistant_response[:100]}...") # 長い場合は切り捨て 188 | 189 | end_time = time.time() 190 | response_time = end_time - start_time 191 | print(f"応答生成時間: {response_time:.2f}秒") 192 | 193 | return GenerationResponse( 194 | generated_text=assistant_response, 195 | response_time=response_time 196 | ) 197 | 198 | except Exception as e: 199 | print(f"シンプル応答生成中にエラーが発生しました: {e}") 200 | traceback.print_exc() 201 | raise HTTPException(status_code=500, detail=f"応答の生成中にエラーが発生しました: {str(e)}") 202 | 203 | def load_model_task(): 204 | """モデルを読み込むバックグラウンドタスク""" 205 | global model 206 | print("load_model_task: モデルの読み込みを開始...") 207 | # load_model関数を呼び出し、結果をグローバル変数に設定 208 | loaded_pipe = load_model() 209 | if loaded_pipe: 210 | model = loaded_pipe # グローバル変数を更新 211 | print("load_model_task: モデルの読み込みが完了しました。") 212 | else: 213 | print("load_model_task: モデルの読み込みに失敗しました。") 214 | 215 | print("FastAPIエンドポイントを定義しました。") 216 | 217 | # --- ngrokでAPIサーバーを実行する関数 --- 218 | def run_with_ngrok(port=8501): 219 | """ngrokでFastAPIアプリを実行""" 220 | nest_asyncio.apply() 221 | 222 | ngrok_token = os.environ.get("NGROK_TOKEN") 223 | if not ngrok_token: 224 | print("Ngrok認証トークンが'NGROK_TOKEN'環境変数に設定されていません。") 225 | try: 226 | print("Colab Secrets(左側の鍵アイコン)で'NGROK_TOKEN'を設定することをお勧めします。") 227 | ngrok_token = input("Ngrok認証トークンを入力してください (https://dashboard.ngrok.com/get-started/your-authtoken): ") 228 | except EOFError: 229 | print("\nエラー: 対話型入力が利用できません。") 230 | print("Colab Secretsを使用するか、ノートブックセルで`os.environ['NGROK_TOKEN'] = 'あなたのトークン'`でトークンを設定してください") 231 | return 232 | 233 | if not ngrok_token: 234 | print("エラー: Ngrok認証トークンを取得できませんでした。中止します。") 235 | return 236 | 237 | try: 238 | ngrok.set_auth_token(ngrok_token) 239 | 240 | # 既存のngrokトンネルを閉じる 241 | try: 242 | tunnels = ngrok.get_tunnels() 243 | if tunnels: 244 | print(f"{len(tunnels)}個の既存トンネルが見つかりました。閉じています...") 245 | for tunnel in tunnels: 246 | print(f" - 切断中: {tunnel.public_url}") 247 | ngrok.disconnect(tunnel.public_url) 248 | print("すべての既存ngrokトンネルを切断しました。") 249 | else: 250 | print("アクティブなngrokトンネルはありません。") 251 | except Exception as e: 252 | print(f"トンネル切断中にエラーが発生しました: {e}") 253 | # エラーにもかかわらず続行を試みる 254 | 255 | # 新しいngrokトンネルを開く 256 | print(f"ポート{port}に新しいngrokトンネルを開いています...") 257 | ngrok_tunnel = ngrok.connect(port) 258 | public_url = ngrok_tunnel.public_url 259 | print("---------------------------------------------------------------------") 260 | print(f"✅ 公開URL: {public_url}") 261 | print(f"📖 APIドキュメント (Swagger UI): {public_url}/docs") 262 | print("---------------------------------------------------------------------") 263 | print("(APIクライアントやブラウザからアクセスするためにこのURLをコピーしてください)") 264 | uvicorn.run(app, host="0.0.0.0", port=port, log_level="info") # ログレベルをinfoに設定 265 | 266 | except Exception as e: 267 | print(f"\n ngrokまたはUvicornの起動中にエラーが発生しました: {e}") 268 | traceback.print_exc() 269 | # エラー後に残る可能性のあるngrokトンネルを閉じようとする 270 | try: 271 | print("エラーにより残っている可能性のあるngrokトンネルを閉じています...") 272 | tunnels = ngrok.get_tunnels() 273 | for tunnel in tunnels: 274 | ngrok.disconnect(tunnel.public_url) 275 | print("ngrokトンネルを閉じました。") 276 | except Exception as ne: 277 | print(f"ngrokトンネルのクリーンアップ中に別のエラーが発生しました: {ne}") 278 | 279 | # --- メイン実行ブロック --- 280 | if __name__ == "__main__": 281 | # 指定されたポートでサーバーを起動 282 | run_with_ngrok(port=8501) # このポート番号を確認 283 | # run_with_ngrokが終了したときにメッセージを表示 284 | print("\nサーバープロセスが終了しました。") -------------------------------------------------------------------------------- /day1/03_FastAPI/python-client.py: -------------------------------------------------------------------------------- 1 | # python_client.py 2 | # このコードは、ngrokで公開されたAPIにアクセスするPythonクライアントの例です 3 | 4 | import requests 5 | import json 6 | import time 7 | 8 | class LLMClient: 9 | """LLM API クライアントクラス""" 10 | 11 | def __init__(self, api_url): 12 | """ 13 | 初期化 14 | 15 | Args: 16 | api_url (str): API のベース URL(ngrok URL) 17 | """ 18 | self.api_url = api_url.rstrip('/') 19 | self.session = requests.Session() 20 | 21 | def health_check(self): 22 | """ 23 | ヘルスチェック 24 | 25 | Returns: 26 | dict: ヘルスチェック結果 27 | """ 28 | response = self.session.get(f"{self.api_url}/health") 29 | return response.json() 30 | 31 | def generate(self, prompt, max_new_tokens=512, temperature=0.7, top_p=0.9, do_sample=True): 32 | """ 33 | テキスト生成 34 | 35 | Args: 36 | prompt (str): プロンプト文字列 37 | max_new_tokens (int, optional): 生成する最大トークン数 38 | temperature (float, optional): 温度パラメータ 39 | top_p (float, optional): top-p サンプリングのパラメータ 40 | do_sample (bool, optional): サンプリングを行うかどうか 41 | 42 | Returns: 43 | dict: 生成結果 44 | """ 45 | payload = { 46 | "prompt": prompt, 47 | "max_new_tokens": max_new_tokens, 48 | "temperature": temperature, 49 | "top_p": top_p, 50 | "do_sample": do_sample 51 | } 52 | 53 | start_time = time.time() 54 | response = self.session.post( 55 | f"{self.api_url}/generate", 56 | json=payload 57 | ) 58 | total_time = time.time() - start_time 59 | 60 | if response.status_code == 200: 61 | result = response.json() 62 | result["total_request_time"] = total_time 63 | return result 64 | else: 65 | raise Exception(f"API error: {response.status_code} - {response.text}") 66 | 67 | # 使用例 68 | if __name__ == "__main__": 69 | # ngrok URLを設定(実際のURLに置き換えてください) 70 | NGROK_URL = "https://your-ngrok-url.ngrok.url" 71 | 72 | # クライアントの初期化 73 | client = LLMClient(NGROK_URL) 74 | 75 | # ヘルスチェック 76 | print("Health check:") 77 | print(client.health_check()) 78 | print() 79 | 80 | # 単一の質問 81 | print("Simple question:") 82 | result = client.generate([ 83 | {"prompt": "AIについて100文字で教えてください"} 84 | ]) 85 | print(f"Response: {result['generated_text']}") 86 | print(f"Model processing time: {result['response_time']:.2f}s") 87 | print(f"Total request time: {result['total_request_time']:.2f}s") -------------------------------------------------------------------------------- /day1/03_FastAPI/requirements.txt: -------------------------------------------------------------------------------- 1 | fastapi 2 | uvicorn 3 | pydantic 4 | transformers 5 | accelerate 6 | sentencepiece 7 | protobuf 8 | pyngrok 9 | -------------------------------------------------------------------------------- /day1/README.md: -------------------------------------------------------------------------------- 1 | # day1 演習用ディレクトリ 2 | 3 | このリポジトリは、StreamlitとFastAPIを使用したアプリケーションの開発および演習用のディレクトリです。 4 | 5 | 演習には、「Google Colab」を使うことを想定しています。 6 | 7 | リポジトリ内の「day1_practice.ipynb」をGoogle Colabで開き、演習を行なってください。 8 | 9 | - [Open with Colab](https://colab.research.google.com/github/matsuolab/lecture-ai-engineering/blob/master/day1/day1_practice.ipynb) 10 | 11 | ## 事前準備 12 | 13 | 演習を行うにあたり、下記の3つのアカウントを利用します。 14 | 15 | 持っていない方は事前に準備することをおすすめします。 16 | 17 | [1] Huggingface 18 | 19 | 1.1 https://huggingface.co/ の右上のSign Upより登録してください。 20 | 21 | 1.2 https://huggingface.co/docs/hub/security-tokens  を読み、アクセストークンの作り方を理解してください。 22 | 23 | 1.3 演習ではgemmaを利用する予定です。ライセンス登録など必要な準備を済ませておいてください。 24 | 25 | https://huggingface.co/google/gemma-2-2b-jpn-it 「Access Gemma on Hugging Face」からライセンスへの同意をする。 26 | 27 | [2] Github 28 | 29 | 2.1 https://docs.github.com/ja/get-started/start-your-journey/creating-an-account-on-github の指示に従ってアカウントを作成してください。 30 | 31 | [3] ngrok 32 | 33 | 3.1 https://dashboard.ngrok.com/signup よりアカウントを作成してください。 34 | 35 | 3.2 https://dashboard.ngrok.com/get-started/your-authtoken より認証済みトークンが取得できることを確認してください。 36 | 37 | ## ディレクトリ構成 38 | 39 | 以下の3つの主要なディレクトリに分かれています。 40 | 41 | ### 01_streamlit_UI 42 | Streamlitの基本的なUI要素を学ぶためのデモアプリケーションが含まれています。 43 | 44 | - **`app.py`**: Streamlitの基本的なUI要素やレイアウトを試すためのサンプルコード。 45 | - **`requirements.txt`**: このアプリケーションを実行するために必要なPythonパッケージ。 46 | 47 | ### 02_streamlit_app 48 | Streamlitを使用したLLM(大規模言語モデル)ベースのチャットボットアプリケーションが含まれています。 49 | 50 | - **`app.py`**: アプリケーションのエントリーポイント。チャット機能、履歴閲覧、サンプルデータ管理のUIを提供します。 51 | - **`ui.py`**: チャットページや履歴閲覧ページなど、アプリケーションのUIロジックを管理します。 52 | - **`llm.py`**: LLMモデルのロードとテキスト生成を行うモジュール。 53 | - **`database.py`**: SQLiteデータベースを使用してチャット履歴やフィードバックを保存・管理します。 54 | - **`metrics.py`**: BLEUスコアやコサイン類似度など、回答の評価指標を計算するモジュール。 55 | - **`data.py`**: サンプルデータの作成やデータベースの初期化を行うモジュール。 56 | - **`config.py`**: アプリケーションの設定(モデル名やデータベースファイル名)を管理します。 57 | - **`requirements.txt`**: このアプリケーションを実行するために必要なPythonパッケージ。 58 | 59 | ### 03_FastAPI 60 | FastAPIを使用し、ローカルLLMをAPIサービス化する内容が含まれています。 61 | 62 | - **`app.py`**: FastAPIを使用してLLMモデルを提供するAPIサーバー。モデルのロード、テキスト生成、ヘルスチェック機能を提供します。 63 | - **`python-client.py`**: FastAPIで提供されるAPIを利用するPythonクライアントのサンプルコード。 64 | - **`requirements.txt`**: このアプリケーションを実行するために必要なPythonパッケージ。 65 | 66 | ## セットアップと実行方法 67 | 68 | ### 1. 必要な依存関係のインストール 69 | 各ディレクトリに移動し、`requirements.txt` を使用して必要なパッケージをインストールしてください。 70 | 71 | ```bash 72 | # 例: 01_streamlit_UI の依存関係をインストール 73 | cd 01_streamlit_UI 74 | pip install -r requirements.txt 75 | ``` 76 | 77 | ### 2. アプリケーションの実行 78 | #### Streamlitアプリケーション 79 | 80 | 01_streamlit_UI または 02_streamlit_app ディレクトリで以下を実行します。 81 | 82 | ```bash 83 | streamlit run app.py 84 | ``` 85 | 86 | #### FastAPIアプリケーション 87 | 88 | 03_FastAPI ディレクトリで以下を実行します。 89 | 90 | ```bash 91 | python app.py 92 | ``` 93 | 94 | ### 3. APIクライアントの使用 95 | 03_FastAPI/python-client.py を実行して、FastAPIで提供されるAPIを利用できます。 96 | 97 | ```bash 98 | python python-client.py 99 | ``` 100 | 101 | ## 使用技術 102 | - Streamlit: インタラクティブなWebアプリケーションを簡単に構築するためのフレームワーク。 103 | - FastAPI: 高速なAPIを構築するためのPythonフレームワーク。 104 | - Transformers: LLMモデルのロードとテキスト生成を行うライブラリ。 105 | - SQLite: チャット履歴やフィードバックを保存するための軽量データベース。 106 | - Pyngrok: ローカルサーバーをインターネットに公開するためのツール。 107 | 108 | ## 注意事項 109 | - FastAPIアプリケーションで使用するモデルは、config.py に指定されたモデル名(例: google/gemma-2-2b-jpn-it)を使用します。モデルのサイズや必要なリソースに注意してください。 110 | - Streamlitアプリケーションでは、フィードバック機能を使用してモデルの回答を評価できます。 111 | -------------------------------------------------------------------------------- /day1/day1_practice.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": { 6 | "id": "gSpnWBP5ELSI" 7 | }, 8 | "source": [ 9 | "# 実践演習 Day 1:streamlitとFastAPIのデモ\n", 10 | "このノートブックでは以下の内容を学習します。\n", 11 | "\n", 12 | "- 必要なライブラリのインストールと環境設定\n", 13 | "- Hugging Faceからモデルを用いたStreamlitのデモアプリ\n", 14 | "- FastAPIとngrokを使用したAPIの公開方法\n", 15 | "\n", 16 | "演習を始める前に、HuggingFaceとngrokのアカウントを作成し、\n", 17 | "それぞれのAPIトークンを取得する必要があります。\n", 18 | "\n", 19 | "\n", 20 | "演習の時間では、以下の3つのディレクトリを順に説明します。\n", 21 | "\n", 22 | "1. 01_streamlit_UI\n", 23 | "2. 02_streamlit_app\n", 24 | "3. 03_FastAPI\n", 25 | "\n", 26 | "2つ目や3つ目からでも始められる様にノートブックを作成しています。\n", 27 | "\n", 28 | "復習の際にもこのノートブックを役立てていただければと思います。\n", 29 | "\n", 30 | "### 注意事項\n", 31 | "「02_streamlit_app」と「03_FastAPI」では、GPUを使用します。\n", 32 | "\n", 33 | "これらを実行する際は、Google Colab画面上のメニューから「編集」→ 「ノートブックの設定」\n", 34 | "\n", 35 | "「ハードウェアアクセラレーター」の項目の中から、「T4 GPU」を選択してください。\n", 36 | "\n", 37 | "このノートブックのデフォルトは「CPU」になっています。\n", 38 | "\n", 39 | "---" 40 | ] 41 | }, 42 | { 43 | "cell_type": "markdown", 44 | "metadata": { 45 | "id": "OhtHkJOgELSL" 46 | }, 47 | "source": [ 48 | "# 環境変数の設定(1~3共有)\n" 49 | ] 50 | }, 51 | { 52 | "cell_type": "markdown", 53 | "metadata": { 54 | "id": "Y-FjBp4MMQHM" 55 | }, 56 | "source": [ 57 | "GitHubから演習用のコードをCloneします。" 58 | ] 59 | }, 60 | { 61 | "cell_type": "code", 62 | "execution_count": null, 63 | "metadata": { 64 | "id": "AIXMavdDEP8U" 65 | }, 66 | "outputs": [], 67 | "source": [ 68 | "!git clone https://github.com/matsuolab/lecture-ai-engineering.git" 69 | ] 70 | }, 71 | { 72 | "cell_type": "markdown", 73 | "metadata": { 74 | "id": "XC8n7yZ_vs1K" 75 | }, 76 | "source": [ 77 | "必要なAPIトークンを.envに設定します。\n", 78 | "\n", 79 | "「lecture-ai-engineering/day1」の配下に、「.env_template」ファイルが存在しています。\n", 80 | "\n", 81 | "隠しファイルのため表示されていない場合は、画面左側のある、目のアイコンの「隠しファイルの表示」ボタンを押してください。\n", 82 | "\n", 83 | "「.env_template」のファイル名を「.env」に変更します。「.env」ファイルを開くと、以下のような中身になっています。\n", 84 | "\n", 85 | "\n", 86 | "```\n", 87 | "HUGGINGFACE_TOKEN=\"hf-********\"\n", 88 | "NGROK_TOKEN=\"********\"\n", 89 | "```\n", 90 | "ダブルクオーテーションで囲まれた文字列をHuggingfaceのアクセストークンと、ngrokの認証トークンで書き変えてください。\n", 91 | "\n", 92 | "それぞれのアカウントが作成済みであれば、以下のURLからそれぞれのトークンを取得できます。\n", 93 | "\n", 94 | "- Huggingfaceのアクセストークン\n", 95 | "https://huggingface.co/docs/hub/security-tokens\n", 96 | "\n", 97 | "- ngrokの認証トークン\n", 98 | "https://dashboard.ngrok.com/get-started/your-authtoken\n", 99 | "\n", 100 | "書き換えたら、「.env」ファイルをローカルのPCにダウンロードしてください。\n", 101 | "\n", 102 | "「01_streamlit_UI」から「02_streamlit_app」へ進む際に、CPUからGPUの利用に切り替えるため、セッションが一度切れてしまいます。\n", 103 | "\n", 104 | "その際に、トークンを設定した「.env」ファイルは再作成することになるので、その手間を減らすために「.env」ファイルをダウンロードしておくと良いです。" 105 | ] 106 | }, 107 | { 108 | "cell_type": "markdown", 109 | "metadata": { 110 | "id": "Py1BFS5RqcSS" 111 | }, 112 | "source": [ 113 | "「.env」ファイルを読み込み、環境変数として設定します。次のセルを実行し、最終的に「True」が表示されていればうまく読み込めています。" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": null, 119 | "metadata": { 120 | "id": "bvEowFfg5lrq" 121 | }, 122 | "outputs": [], 123 | "source": [ 124 | "!pip install python-dotenv\n", 125 | "from dotenv import load_dotenv, find_dotenv\n", 126 | "\n", 127 | "%cd /content/lecture-ai-engineering/day1\n", 128 | "load_dotenv(find_dotenv())" 129 | ] 130 | }, 131 | { 132 | "cell_type": "markdown", 133 | "metadata": { 134 | "id": "os0Yk6gaELSM" 135 | }, 136 | "source": [ 137 | "# 01_streamlit_UI\n", 138 | "\n", 139 | "ディレクトリ「01_streamlit_UI」に移動します。" 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": null, 145 | "metadata": { 146 | "id": "S28XgOm0ELSM" 147 | }, 148 | "outputs": [], 149 | "source": [ 150 | "%cd /content/lecture-ai-engineering/day1/01_streamlit_UI" 151 | ] 152 | }, 153 | { 154 | "cell_type": "markdown", 155 | "metadata": { 156 | "id": "eVp-aEIkELSM" 157 | }, 158 | "source": [ 159 | "必要なライブラリをインストールします。" 160 | ] 161 | }, 162 | { 163 | "cell_type": "code", 164 | "execution_count": null, 165 | "metadata": { 166 | "id": "nBe41LFiELSN" 167 | }, 168 | "outputs": [], 169 | "source": [ 170 | "%%capture\n", 171 | "!pip install -r requirements.txt" 172 | ] 173 | }, 174 | { 175 | "cell_type": "markdown", 176 | "metadata": { 177 | "id": "Yyw6VHaTELSN" 178 | }, 179 | "source": [ 180 | "ngrokのトークンを使用して、認証を行います。" 181 | ] 182 | }, 183 | { 184 | "cell_type": "code", 185 | "execution_count": null, 186 | "metadata": { 187 | "id": "aYw1q0iXELSN" 188 | }, 189 | "outputs": [], 190 | "source": [ 191 | "!ngrok authtoken $$NGROK_TOKEN" 192 | ] 193 | }, 194 | { 195 | "cell_type": "markdown", 196 | "metadata": { 197 | "id": "RssTcD_IELSN" 198 | }, 199 | "source": [ 200 | "アプリを起動します。" 201 | ] 202 | }, 203 | { 204 | "cell_type": "code", 205 | "execution_count": null, 206 | "metadata": { 207 | "id": "f-E7ucR6ELSN" 208 | }, 209 | "outputs": [], 210 | "source": [ 211 | "from pyngrok import ngrok\n", 212 | "\n", 213 | "public_url = ngrok.connect(8501).public_url\n", 214 | "print(f\"公開URL: {public_url}\")\n", 215 | "!streamlit run app.py" 216 | ] 217 | }, 218 | { 219 | "cell_type": "markdown", 220 | "metadata": { 221 | "id": "kbYyXVFjELSN" 222 | }, 223 | "source": [ 224 | "公開URLの後に記載されているURLにブラウザでアクセスすると、streamlitのUIが表示されます。\n", 225 | "\n", 226 | "app.pyのコメントアウトされている箇所を編集することで、UIがどの様に変化するか確認してみましょう。\n", 227 | "\n", 228 | "streamlitの公式ページには、ギャラリーページがあります。\n", 229 | "\n", 230 | "streamlitを使うとpythonという一つの言語であっても、様々なUIを実現できることがわかると思います。\n", 231 | "\n", 232 | "https://streamlit.io/gallery" 233 | ] 234 | }, 235 | { 236 | "cell_type": "markdown", 237 | "metadata": { 238 | "id": "MmtP5GLOELSN" 239 | }, 240 | "source": [ 241 | "後片付けとして、使う必要のないngrokのトンネルを削除します。" 242 | ] 243 | }, 244 | { 245 | "cell_type": "code", 246 | "execution_count": null, 247 | "metadata": { 248 | "id": "8Ek9QgahELSO" 249 | }, 250 | "outputs": [], 251 | "source": [ 252 | "from pyngrok import ngrok\n", 253 | "ngrok.kill()" 254 | ] 255 | }, 256 | { 257 | "cell_type": "markdown", 258 | "metadata": { 259 | "id": "o-T8tFpyELSO" 260 | }, 261 | "source": [ 262 | "# 02_streamlit_app" 263 | ] 264 | }, 265 | { 266 | "cell_type": "markdown", 267 | "metadata": { 268 | "id": "QqogFQKnELSO" 269 | }, 270 | "source": [ 271 | "\n", 272 | "ディレクトリ「02_streamlit_app」に移動します。" 273 | ] 274 | }, 275 | { 276 | "cell_type": "code", 277 | "execution_count": null, 278 | "metadata": { 279 | "id": "UeEjlJ7uELSO" 280 | }, 281 | "outputs": [], 282 | "source": [ 283 | "%cd /content/lecture-ai-engineering/day1/02_streamlit_app" 284 | ] 285 | }, 286 | { 287 | "cell_type": "markdown", 288 | "metadata": { 289 | "id": "-XUH2AstELSO" 290 | }, 291 | "source": [ 292 | "必要なライブラリをインストールします。" 293 | ] 294 | }, 295 | { 296 | "cell_type": "code", 297 | "execution_count": null, 298 | "metadata": { 299 | "id": "mDqvI4V3ELSO" 300 | }, 301 | "outputs": [], 302 | "source": [ 303 | "%%capture\n", 304 | "!pip install -r requirements.txt" 305 | ] 306 | }, 307 | { 308 | "cell_type": "markdown", 309 | "metadata": { 310 | "id": "ZO31umGZELSO" 311 | }, 312 | "source": [ 313 | "ngrokとhuggigfaceのトークンを使用して、認証を行います。" 314 | ] 315 | }, 316 | { 317 | "cell_type": "code", 318 | "execution_count": null, 319 | "metadata": { 320 | "id": "jPxTiEWQELSO" 321 | }, 322 | "outputs": [], 323 | "source": [ 324 | "!ngrok authtoken $$NGROK_TOKEN\n", 325 | "!huggingface-cli login --token $$HUGGINGFACE_TOKEN" 326 | ] 327 | }, 328 | { 329 | "cell_type": "markdown", 330 | "metadata": { 331 | "id": "dz4WrELLELSP" 332 | }, 333 | "source": [ 334 | "stramlitでHuggingfaceのトークン情報を扱うために、streamlit用の設定ファイル(.streamlit)を作成し、トークンの情報を格納します。" 335 | ] 336 | }, 337 | { 338 | "cell_type": "code", 339 | "execution_count": null, 340 | "metadata": { 341 | "id": "W184-a7qFP0W" 342 | }, 343 | "outputs": [], 344 | "source": [ 345 | "# .streamlit/secrets.toml ファイルを作成\n", 346 | "import os\n", 347 | "import toml\n", 348 | "\n", 349 | "# 設定ファイルのディレクトリ確保\n", 350 | "os.makedirs('.streamlit', exist_ok=True)\n", 351 | "\n", 352 | "# 環境変数から取得したトークンを設定ファイルに書き込む\n", 353 | "secrets = {\n", 354 | " \"huggingface\": {\n", 355 | " \"token\": os.environ.get(\"HUGGINGFACE_TOKEN\", \"\")\n", 356 | " }\n", 357 | "}\n", 358 | "\n", 359 | "# 設定ファイルを書き込む\n", 360 | "with open('.streamlit/secrets.toml', 'w') as f:\n", 361 | " toml.dump(secrets, f)" 362 | ] 363 | }, 364 | { 365 | "cell_type": "markdown", 366 | "metadata": { 367 | "id": "fK0vI_xKELSP" 368 | }, 369 | "source": [ 370 | "アプリを起動します。\n", 371 | "\n", 372 | "02_streamlit_appでは、Huggingfaceからモデルをダウンロードするため、初回起動には2分程度時間がかかります。\n", 373 | "\n", 374 | "この待ち時間を利用して、app.pyのコードを確認してみましょう。" 375 | ] 376 | }, 377 | { 378 | "cell_type": "code", 379 | "execution_count": null, 380 | "metadata": { 381 | "id": "TBQyTTWTELSP" 382 | }, 383 | "outputs": [], 384 | "source": [ 385 | "from pyngrok import ngrok\n", 386 | "\n", 387 | "public_url = ngrok.connect(8501).public_url\n", 388 | "print(f\"公開URL: {public_url}\")\n", 389 | "!streamlit run app.py" 390 | ] 391 | }, 392 | { 393 | "cell_type": "markdown", 394 | "metadata": {}, 395 | "source": [ 396 | "アプリケーションの機能としては、チャット機能や履歴閲覧があります。\n", 397 | "\n", 398 | "これらの機能を実現するためには、StreamlitによるUI部分だけではなく、SQLiteを使用したチャット履歴の保存やLLMのモデルを呼び出した推論などの処理を組み合わせることで実現しています。\n", 399 | "\n", 400 | "- **`app.py`**: アプリケーションのエントリーポイント。チャット機能、履歴閲覧、サンプルデータ管理のUIを提供します。\n", 401 | "- **`ui.py`**: チャットページや履歴閲覧ページなど、アプリケーションのUIロジックを管理します。\n", 402 | "- **`llm.py`**: LLMモデルのロードとテキスト生成を行うモジュール。\n", 403 | "- **`database.py`**: SQLiteデータベースを使用してチャット履歴やフィードバックを保存・管理します。\n", 404 | "- **`metrics.py`**: BLEUスコアやコサイン類似度など、回答の評価指標を計算するモジュール。\n", 405 | "- **`data.py`**: サンプルデータの作成やデータベースの初期化を行うモジュール。\n", 406 | "- **`config.py`**: アプリケーションの設定(モデル名やデータベースファイル名)を管理します。\n", 407 | "- **`requirements.txt`**: このアプリケーションを実行するために必要なPythonパッケージ。" 408 | ] 409 | }, 410 | { 411 | "cell_type": "markdown", 412 | "metadata": { 413 | "id": "Xvm8sWFPELSP" 414 | }, 415 | "source": [ 416 | "後片付けとして、使う必要のないngrokのトンネルを削除します。" 417 | ] 418 | }, 419 | { 420 | "cell_type": "code", 421 | "execution_count": null, 422 | "metadata": { 423 | "id": "WFJC2TmZELSP" 424 | }, 425 | "outputs": [], 426 | "source": [ 427 | "from pyngrok import ngrok\n", 428 | "ngrok.kill()" 429 | ] 430 | }, 431 | { 432 | "cell_type": "markdown", 433 | "metadata": { 434 | "id": "rUXhIzV7ELSP" 435 | }, 436 | "source": [ 437 | "# 03_FastAPI\n", 438 | "\n", 439 | "ディレクトリ「03_FastAPI」に移動します。" 440 | ] 441 | }, 442 | { 443 | "cell_type": "code", 444 | "execution_count": null, 445 | "metadata": { 446 | "id": "4ejjDLxr3kfC" 447 | }, 448 | "outputs": [], 449 | "source": [ 450 | "%cd /content/lecture-ai-engineering/day1/03_FastAPI" 451 | ] 452 | }, 453 | { 454 | "cell_type": "markdown", 455 | "metadata": { 456 | "id": "f45TDsNzELSQ" 457 | }, 458 | "source": [ 459 | "必要なライブラリをインストールします。" 460 | ] 461 | }, 462 | { 463 | "cell_type": "code", 464 | "execution_count": null, 465 | "metadata": { 466 | "id": "9uv6glCz5a7Z" 467 | }, 468 | "outputs": [], 469 | "source": [ 470 | "%%capture\n", 471 | "!pip install -r requirements.txt" 472 | ] 473 | }, 474 | { 475 | "cell_type": "markdown", 476 | "metadata": { 477 | "id": "JfrmE2VmELSQ" 478 | }, 479 | "source": [ 480 | "ngrokとhuggigfaceのトークンを使用して、認証を行います。" 481 | ] 482 | }, 483 | { 484 | "cell_type": "code", 485 | "execution_count": null, 486 | "metadata": { 487 | "id": "ELzWhMFORRIO" 488 | }, 489 | "outputs": [], 490 | "source": [ 491 | "!ngrok authtoken $$NGROK_TOKEN\n", 492 | "!huggingface-cli login --token $$HUGGINGFACE_TOKEN" 493 | ] 494 | }, 495 | { 496 | "cell_type": "markdown", 497 | "metadata": { 498 | "id": "t-wztc2CELSQ" 499 | }, 500 | "source": [ 501 | "アプリを起動します。\n", 502 | "\n", 503 | "「02_streamlit_app」から続けて「03_FastAPI」を実行している場合は、モデルのダウンロードが済んでいるため、すぐにサービスが立ち上がります。\n", 504 | "\n", 505 | "「03_FastAPI」のみを実行している場合は、初回の起動時にモデルのダウンロードが始まるので、モデルのダウンロードが終わるまで数分間待ちましょう。" 506 | ] 507 | }, 508 | { 509 | "cell_type": "code", 510 | "execution_count": null, 511 | "metadata": { 512 | "id": "meQ4SwISn3IQ" 513 | }, 514 | "outputs": [], 515 | "source": [ 516 | "!python app.py" 517 | ] 518 | }, 519 | { 520 | "cell_type": "markdown", 521 | "metadata": { 522 | "id": "RLubjIhbELSR" 523 | }, 524 | "source": [ 525 | "FastAPIが起動すると、APIとクライアントが通信するためのURL(エンドポイント)が作られます。\n", 526 | "\n", 527 | "URLが作られるのと合わせて、Swagger UIというWebインターフェースが作られます。\n", 528 | "\n", 529 | "Swagger UIにアクセスすることで、APIの仕様を確認できたり、APIをテストすることができます。\n", 530 | "\n", 531 | "Swagger UIを利用することで、APIを通してLLMを動かしてみましょう。" 532 | ] 533 | }, 534 | { 535 | "cell_type": "markdown", 536 | "metadata": { 537 | "id": "XgumW3mGELSR" 538 | }, 539 | "source": [ 540 | "後片付けとして、使う必要のないngrokのトンネルを削除します。" 541 | ] 542 | }, 543 | { 544 | "cell_type": "code", 545 | "execution_count": null, 546 | "metadata": { 547 | "id": "RJymTZio-WPJ" 548 | }, 549 | "outputs": [], 550 | "source": [ 551 | "from pyngrok import ngrok\n", 552 | "ngrok.kill()" 553 | ] 554 | } 555 | ], 556 | "metadata": { 557 | "colab": { 558 | "provenance": [] 559 | }, 560 | "kernelspec": { 561 | "display_name": "Python 3", 562 | "name": "python3" 563 | }, 564 | "language_info": { 565 | "name": "python" 566 | } 567 | }, 568 | "nbformat": 4, 569 | "nbformat_minor": 0 570 | } 571 | -------------------------------------------------------------------------------- /day3/README.md: -------------------------------------------------------------------------------- 1 | # day3 演習用ディレクトリ 2 | 3 | 第3回「機械学習システムの改善」に関する演習用のディレクトリです。 4 | 5 | # 演習の目的 6 | 7 | 本演習コンテンツでは、技術的な用語や仕組み(例:RAG におけるチャンク処理など)の詳細な理解を目的とするのではなく、AI の性能を向上させるための方法を実際に体験することを主眼としています。 8 | この体験を通じて、複数のデータソースの扱いや、AI に対する適切な入力・文脈の与え方など、実践的なスキルへの理解を深めることを目的とします。 9 | 10 | # 演習の内容 11 | 主な演習の流れは以下のようになっています。 12 | 13 | 1. ベースモデルのそのままの状態で質問に回答させる 14 | 2. 講義の文字起こしデータを使う 15 | 3. チャンク化を行い、文字情報を分割する 16 | 4. Rerankにより、質問に関連する内容を取り出せるようにする 17 | 5. さらに改善方法を検討する 18 | 19 | 演習の詳細はノートブックに記載されています。 20 | 21 | 演習には、「Google Colab」を使うことを想定しています。 22 | 23 | 講義内の演習の時間では、ディレクトリ内の「ai_engineering_03.ipynb」を使用しています。 24 | このノートブックでは、Google ColabのL4というGPUを使用する想定で作られています。 25 | 26 | - [Open with Colab(GPU:L4)](https://colab.research.google.com/github/matsuolab/lecture-ai-engineering/blob/master/day3/ai_engineering_03.ipynb) 27 | 28 | ## 注意事項 29 | L4はGoogle Colabの無料利用では選択できない可能性があります。 30 | 31 | 演習として説明を行う都合上、講師の環境では「L4」を使用しますが、受講生の方で「L4」を使用できない場合は、 32 | 無料で利用可能な「T4」向けのノートブック「ai_engineering_03_T4.ipynb」も用意したのでこちらを使用して演習を行なってください。 33 | 34 | - [Open with Colab(GPU:T4)](https://colab.research.google.com/github/matsuolab/lecture-ai-engineering/blob/master/day3/ai_engineering_03_T4.ipynb) 35 | 36 | 演習の大まかな流れはどちらのノートブックでも同じように体験できるように構成していますが、細かい部分で内容が異なる場合がある点はご了承をお願いします。 37 | (主な違いとしては、T4のノートブックで利用するモデルの方がサイズが小さいことと、演習として実際に試せる内容が少ない(「5. さらに改善方法を検討する」はGPUメモリが不足して実行できない)などの違いがあります。) 38 | 39 | # 事前準備 40 | 事前準備の内容は、L4向けのノートブックと、T4向けのノートブックで異なります。 41 | 42 | 使うノートブックに合わせて事前準備を行なってください。 43 | 44 | ## L4向けのノートブックの場合 45 | 1. Huggingfaceのアクセストークンの取得 46 | ノートブック内でHugginfaceのアクセストークンを使用します。 47 | 48 | https://huggingface.co/settings/tokens 49 | 50 | 2. Llama3モデルのライセンス 51 | 演習ではLlama3を利用する予定です。ライセンス登録など必要な準備を済ませておいてください。 52 | 53 | Huggingfaceのアカウントでログインした状態で以下のモデルカードのページにアクセスし、「META LLAMA 3 COMMUNITY LICENSE AGREEMENT」からライセンスへの同意をする。 54 | 55 | https://huggingface.co/meta-llama/Meta-Llama-3-8B-Instruct 56 | 57 | ## T4向けのノートブックの場合 58 | 1. Huggingfaceのアクセストークンの取得 59 | ノートブック内でHugginfaceのアクセストークンを使用します。 60 | 61 | https://huggingface.co/settings/tokens 62 | 63 | 2. Gemma2モデルのライセンス 64 | day1で使用したモデルと同じなので、day1で既にライセンスに同意している方は以下の手順はスキップできます。 65 | 66 | Huggingfaceのアカウントでログインした状態で以下のモデルカードのページにアクセスし、「Access Gemma on Hugging Face」からライセンスへの同意をする。 67 | 68 | https://huggingface.co/google/gemma-2-2b-jpn-it 69 | 70 | # 演習に関連する参考情報 71 | 72 | ## データを綺麗にするには 73 | - 会話データの場合、文書として書かれたデータよりもデータが扱いにくい場合が多いです。そのため、データをクレンジングすると扱いやすいデータになることが多いです。 74 | - 演習の内容にもある通り、誤字や文の区切りなどを調整する。 75 | - 不要な情報を減らす(生成したい内容や目的に合わせて調整する)。 76 | - 重複した文章の削除する。 77 | - 表現方法の統一する(少し違う言い回しに変えるなどを会話においては行うことがある)。 78 | 79 | ## データソースを追加するには 80 | - テキスト生成モデルに対してデータソースを使用する場合、データをモデルにプロンプトとして投入する都合により、データを最終的にはテキストデータに変換することになります。 81 | - そのため、PDFやWeb上のHTML等をテキストに変換(パース)する方法についても調べてみましょう。 82 | - PDFやWebなどのデータを取得したり、利用したりする際には、データの提供元の利用規約等を確認し、準拠した上で利用するように習慣を作りましょう。 83 | - PDFの例 84 | - https://pypi.org/project/PyPDF2/ 85 | - https://pypi.org/project/pdfminer.six/ 86 | - HTMLの例 87 | - https://docs.python.org/ja/3/library/html.parser.html 88 | - SaaSとしてWebデータをLLMが処理しやすくするAPIを提供している企業もあります。 89 | - [https://jina.ai](https://jina.ai/) 90 | - 使用したいWebページのURLをAPIに設定してリクエストを投げると、Webページ上のテキスト等をLLMが処理しやすい形に変換してくれたりします(HTMLのパースをする手間を肩代わりしてくれる)。 91 | 92 | ## モデルを変えるとどうなるか 93 | - T4版のノートブックでは、ベースモデルとしてgemma2を使っています。 94 | - Llama3よりもパラメータ数が少なく、モデルもLlama3と比べ古いので、生成文は少しぎこちない印象はあります。gemma2ではsystemプロンプトを使えないため、推論のさせ方もLlamaと比べて少し異なっています。 95 | - 埋め込みモデルを変えるという考え方もあります。埋め込み時の結果が変わるので、参考資料としてプロンプトに入ってくる内容が変わることが考えられます。 96 | 97 | ## 発展的な手法にどのようなものがあるか 98 | - RAGという1つのテーマだけでも様々なアプローチがあります。 99 | - https://github.com/jxzhangjhu/Awesome-LLM-RAG -------------------------------------------------------------------------------- /day3/data/LLM2024_day4.txt: -------------------------------------------------------------------------------- 1 | 早速内容ですけど、目的はタイトルの通りですけど言語モデルスケール則について学ぶってことで、大規模言語モデルっていうふうに呼ばれてますけど、ちょっとスケール則の話とか初回も少ししましたけど、これだけ大きくなっている一つの理由になってますのでそのスケール則ってどういうものなのかとかそれがなぜ重要なのかっていうところ、説明できるようなってもらうというところと、スケール則ってどうやって求めるんでしたっけというところを説明実装できるようになるところについて中心的に話していければと思ってます。 2 | あのスケールするっていうところではタイトルの通りなんですけど、ちょっとこれスケーリングPretraining回ってなってるんですけれども、ちょっと最近はですね、このPretrainingだけではなくて、推論をスケールさせるというような話も出てきてましてせっかくなのでその最近の話題ということです推論時のスケーリングことで、ちょっとタイトル詐欺が入ってるんですけどPretrainingだけじゃない、スケーリングも扱うということで、ちょっと若干あのタイトル詐欺なんですけども、あの最近の話題ということで推論時のスケジュールについても話していきたいなと思っています。 3 | 演習では2つ目のポイントに近いですけどスケール則を実際に求めるというところでそのコードを実装できるようになってもらうということを目的としています。では早速ですけど中身に入っていきたいと思います。ちなみ余談なんですけどこれ実はDay4が去年から結構変わってまして、去年Day4だったものをDay4とDay4とDay8に分けてるんですけども、分けたはずのDay4がなぜか90枚スライドがあるという状況でしてちょっと若干早口になって申し訳ないんすけど少し資料を補足としてやっていただきながら自学できるようにしてあると思いますんで、何かわかんないとこあったら資料読んでもらえると思います。 4 | 5 | 6 | 目標の通りですけれども大きく四つの話をしたいと思ってます。最初がスケール則とは何かそもそもどういうものだったかっていうところをご説明したいと思ってます。何かっていうのを先に学んだ後に、なんでこれ学ばなきゃいけないのか、これ知ってると何のいいことがあるのっていう意味でスケール則どういうふうに使ってるのか、他にどういうふうに使われてるのかってとこについて、2個目でお話したいと思います。 7 | 三つ目がそれをどうやってまとめるとか、演習に関係するところですね。最後に新しいトレンドっていうことでちょっと時間の関係で若干駆け足なるかもしれないけど、推論時のフィーリングっていう最近のo1とかも出てますけど、その辺で使われている技術について、正確には使われているとされている技術についてちょっとお話したいなと思っています。 8 | 9 | まずスケール則とは何かっていうところですね。 10 | あの最初にも言った通りですけれど、スケールっていうのは、その大規模言語モデルを支える一つの大きな要素になってます。言語モデルっていうところがDay3でやりましたけれども、最近はTrasnformerになっていて、Transformerの中にもいろいろ種類があるということで発展的課題がまたコミュニティの方であるってありましたけども、基本的にはこの言語モデルを作る技術について言語ってどういうものですかっていうのをDay3でパターンだかなと思います。 11 | Day4ではこの大規模っていう側にフォーカスを当てまして、どうスケールさせるのかとか、なんでスケールさせることが有効なのかそれを支えるあのスケール則っていうものについてお話したいと思います。講義全体の位置づけも少しおさらいしておきますとます今日までが行った事前学習と呼ばれるような枠組みの話になります。 12 | また非常に大規模に学習データで訓練するっていうプロセスです。そっから次回以降はFine-Tuningってことで少しまた違う話をしますので一旦今日までがあの話の区切りだと思ってもらえればいいかなと思います。 13 | 14 | 15 | 中身に入っていきます。こちらのこのスライド初回にもお見せした通りですけれども、大規模言語モデルと呼ばれるものが進展していますということですあのこのサーベイ論文紹介にも紹介した通りですけど2023年の間に13回ぐらい更新されるぐらいモデルがどんどん出てると、2024年だって今年になってもいろいろなモデルが出たり、なんか巷で試されて実はこれは良くないんじゃないかと言われたりいろいろしているというような状況になっています。 16 | それからこれも最初紹介に示した通りですけども、基本的にこの裏側にはスケール化というのが進んできています。元々2018年ぐらいが117Billonだったのが、2020年には175Billonと1000倍ぐらいです企画なっていてGPT-4は1Billon以上だというふうに言われています。 17 | このスケールっていうのがやっぱ重要な要素になってきています。 18 | 19 | こういうスケールしてるわけですけど、何でこんなにスケールしてるのかと、あるいはこのスケールするってどういうことなのかっていうのを今日は話していきたいと思います。 20 | 特にですね背景にあるのが一番よく出てくるのがスケール則と呼ばれる経験則でして、最初の方ではこの二つの論文を中心に説明したいと思います。一つがOpenAIが2020年に出した論文でして、Scaling Laws for Neural Language Modelというような論文になっています。 21 | これちなみにGPT3が2020年、このスケーリング則がでたあの約半年後ぐらいに出てまして、GPT4の中でもこのスケーリング則の話とか出てくるようなものになってまして基本的にこのGPT3が開発された背景に、こういうスケール則の発見というのがあったというふうに言われています。 22 | もう一つがこれを少し拡張した形の研究としてTraining Compute-Optimal Language Modelというような研究があります。これ、当時のDeepMind今GoogleDeepMindですけど、が出した論文でしてこのスケール則の考え方を使って、どういうふうなデータと、どういうふうなモデルサイズを用意すれば良いモデルが作れるのかっていうのを検証した論文になってます。 23 | この辺り中心にいろいろな研究をされながら今日はご説明できればと思ってます。 24 | 25 | 早速スケール則とは何かっていうところについてこのスライド自体は初回に多分お見せしたかなと思いますけれども、どういうものかっていうのをおさらいすると、基本的な計算資源とデータサイズ厳密にtoken数ですね、学習に使っているtokenの数とモデルのパラメータ数っていうのと誤差、その学習に使ってる誤差ですね。 26 | 正確にいうとテストデータにおける誤差の間にある経験則が存在するというようなことを指したあの言葉になってます。どういう経験則かというと、例えばわかりやすいのが右側のパラメータなのでパラメータの方に注目すると、この青い点を打つっていうのが、実際にどれぐらいのパラメータを使ったときに、どれぐらいの損失だったかっていうのを表してますけれども、これをプロットすると、両対数グラフ要は10のN乗みたいな形で書いたときですね、こういった綺麗な直線になるというような法則になってますね。 27 | これがパラメータだけじゃなくて、データサイズだったり、計算量に対しても、横軸をそれぞれ変えてやったときに、同じように、こういった対数空間上での線形のスケールが成立しますよというような経験則になってます。 28 | 何か細かいことを一応書いてますけど一応これ要は点が実測値になってまして、この実線みたいなのが、予測値に該当してます。これあとは他の2変数は十分に大きいと仮定したときのあの実験結果になってます。 29 | 30 | 31 | 一つ一つ見ていきますと今見たのと同じ順の話ですけどパラメータ数についての図をピックアップしたのがこの図にあります。さっきと同じで10の5乗,10の7乗,10の9乗って形で横軸が対数の対数グラフになってまして、例えばCross-Entropyで対数化された値になっています。 32 | これを見ていくと非常に綺麗に、ちょっとガタガタ若干してますけど、巨視的に見ると、大体この黒いフィッティングした線と同じになっています。 33 | それから次の図がデータセットのところについてピックアップしたものですけれど、さっき言った通りデータサイズって言ってるのは基本的なtokenの数だと思ってください。10の8乗tokenとか10の9乗tokenっていう単位これについてもパラメータと同様に点の値をプロットしていって、その間をフィッティングしてると非常に綺麗な関係が見えることがわかるかと思います。 34 | 最後が計算資源でして計算資源だけちょっとわかりにくいんですけどまず横軸がこのPeta FLOPs Daysというのになってます。Peta FLOPs Daysっていうのが何かっていうのを説明しますと、一応補足というか知ってる人は聞き流してもらえばと思いますけど、計算ってのはどれぐらいの浮動小数点演算を行っているかを表してる単位だと思ってください。 35 | 36 | 37 | 浮動小数点演算っていうのはコンピュータしてる人だと分かると思いますけど、パラメータの足し算とか掛け算に該当してまして、細かい話するとディープラーニングの場合だと普通FP32とか1Byteですね、の計算が使われるので1Byte同士の足し算みたいのが1演算、で掛け算も1演算という形で表せるになってます。 38 | これなんか右下に巨大な巨大な2階建てMLPって書いてますけど、このWをかけるみたいな、これが掛け算に相当しますし、例えばバイアスを足すみたいなもバイアスを足すということで基本的にニューラルネットワークの計算ってのはこの浮動小数点のパラメータを足したりかけたりするというので表すことからできることがわかると思います。 39 | 「紛らわしいか」っていうのちょっといっぱい無視してもらえばいいと思うんすけど、この合計の浮動小数点演算の数を表すものとして、FLOPsというのが使われています。 40 | さっき横軸がこの要は浮動小数点演算の合計の数になっているというふうに理解いただければと思います。ちなみに「紛らわしい」て書いたのが大文字の「S」のFLOPSっていうのもありましてこれがFloating Points Operation per Secondsっていうものになってまして、1秒間当たりどれぐらい計算できるかっていうのものになってます。 41 | これあのGPUの仕様書とか見ると、こういう感じでFP64とかFP32で例えばこれ19.5TFLOPSみたいな書いてあるんすけどこれは要は、1秒間当たり何回計算できるか、基本的にはハードウェアの計算能力を表す単位だと思ってもらえばと思います。ちょっと戻りますけど、ちっちゃい小文字のsは全体でどれだけ浮動小数点演算が必要かというものを表すもの。と若干違うということでご理解いただければ。要はあのスケール則を見たときに別に演算能力が上がってるわけではなくて合計で必要な計算量が上がってるというふうに理解いただければいい。 42 | 43 | 44 | あのスケール則の方に戻りますと、パラメーターの話とパラメータとデータサイズ(token数)の話と若干違うとこがありまして、点じゃなくてこういうなんか曲線が打たれてます。 45 | あのこの曲線っていうのが、あのこの図では、あの異なるモデルサイズで学習したときの学習曲線をイメージして意味しています。要は横にいくほどだんだん計算量がかかってくるので、学習したときのこの1個1個の学習曲線がこの青いなんか薄い線に対応してると思ってください。 46 | これ例えばこの特定のパラメータで学習したときの曲線がこの1個の中に該当するということで、あの別のものが違う線、でさらに違うものが、これ例えばN''が一番大きいので計算量が掛かる。N'が真ん中ぐらいでこれぐらいの計算。Nがこれぐらいで計算するっていうような意味合いです。このようにを変えてやったときにこのオレンジの線っていうのはこの一番ベストな条件、この計算量を取ったときに一番いい条件で学習した時の値っていうのがこのオレンジの点線でフィッティングされてるというふうに理解いただければ。これモデルサイズが小さいときは最初早く学習が進むわけですね計算量が少なくても性能が出るので計算量が少ない前提だと、モデルサイズがちっちゃい方がいい。 47 | 逆にモデルサイズが大きいと、なかなか学習が進まないんですけど、最終的な性能が良くなるということでこれが最適なレートみたいのが存在するということです。ちなみにこれは読み方というか使い方に近いですけれど横軸テストロスをある値にしたときに、どういうものが良いモデルかっていうのは、この横をピーっといけば大体わかって、これちょっとパラメータ数がいくつかってのはわからないですけどこれN'ぐらいがいいってことで、めちゃめちゃ大きければいいというわけでも小さければいいわけではないことがわかりますし、自分が計算資源どれぐらい持ってるとき、何かパソコンを何か100台ぐらい持ってますとか何か自分が使える計算量がわかってるときにも、これをぴーっと線をひいてると大体これぐらいのパラメータを取ればいいんだなってことでそれがわかるということになります。 48 | 49 | 50 | これがあのスケール則の詳細ですね。コンピュートのところ以外は割とわかりやすいですけれど、こういった関係が成立するってのがスケール則と呼ばれているものです。若干細かい補足なんですけれどというかあの、さっきからこの謎の値が書いてあったと思うんすけど一応詳細に見ておくと補足なんで理解した人だけでいいですけれどこれよく見るとこういうべき乗の関係になっていることがあります。 51 | このLっていうのがロスですね、最適化したりとか小さくしたいもので、ちょっとこれ若干順番が違うんですがX_cっていうのが、この例えば2.3掛け10の8乗みたいな、これがコンピュートの場合ですけど、係数に該当していて、アルファがこのマイナス0.05みたいな対応するということでこれ今コンピュートの図だけ出してますけど、他のものも同じような形をしています。 52 | これ何で両対数にすると線形になるのかっていうことですけどこれログ取れば明らかで、どっちもログ取るとこういう感じになるので、logX_cがこれ横軸なんで線型の普通の一次の式で、αが傾きの線みたいな感じになることがわかります。αlogX_cが接線みたいなもんですね。そういった形になるので、このスケール則というのはこういうこのべき乗の形で書けるのでスケール則というふうに言われています。 53 | 54 | 55 | ここまでスケール則の話をしてきましたけど、あのそうですねモデルサイズがまだ小さい状況でのスケール則っていうのを検証してましたけど、さっき半年後ぐらいにGPT3が出ましたよって言いましたけど、GPT3でもこのスケール則ってのを検証しているってことが報告されてます。 56 | さっきのあの学習のときと同じような図があの論文には載っていて、これ色がそれぞれパラメータの違いに該当しています。黄色が一番大きくて青が一番ちっちゃい、紫かな、ちっちゃいモデルになってます。先行研究、さっきの今まで話してたものより2桁オーダーが大きいモデルにおいても、これ厳密に言うとまだ収束してないように見えるんですけど、おおむねスケール則が成立してるっていうことがGPT3の論文だと報告されています。 57 | 58 | 59 | あのスケール則についてここまで他の多分一番有名なのが先ほどのから話しているScaling Laws for Neural Language Modelというものなんですけれど、実はこのスケーリングっていうの自体、スケーリング則、スケール則が成立するってこと自体は、もうちょっと前から知られていたというふうに言われています。 60 | ちょっと僕ももしかしたらもっと昔からあるかもしれないんで僕が知ってる限りですが、少なくとも2017年の論文では検証されているということが言われてます。このDeep Learning Scaling is Predictable Empiricallyという論文があります。 61 | ちなみにこれ今日話さないんですけど理論的にどういう条件だとスケール則が成立するのかっていうのを議論していたりもするので、もし興味ある人はこの論文化後か、あとはスタンフォードのレクチャーでこの辺触れられていたのでもし興味ある人は見てみると良いと思います。 62 | 少しだけ内容に触れますと、この論文では当然2017年なんでこれTrasnformerがあの出たか出ないかぐらいの時期なのであのTrasnformerのスケール則っていうのはやってなくて、LSTMだったり、RHNというか、リカレントハイウェイネットワークっていうちょっと何ていうか、LSTMの亜種みたいなものをですね、時系列のResidualネットワークみたいなことを使った研究になってます。 63 | これ見てもらうと横軸がトレーニングデータセットのサイズ、縦軸がロスですね、ログスケールのロスになってまして、さっき見たのと同じような横線の図が広がっているというふうに描かれていることがわかるかと思います。 64 | 厳密に言語モデルじゃなくてMachine Translationの結果だったということです。あとは異なるとして対象モデルが違うってことでTrasnformerじゃないモデルを使ってますよとか規模も全然違うものですけれども、初期的にはこういった結果も知られていました。 65 | それをスケールアップさせたのが先ほどのOpenAIの研究だというふうに説明できるかなと思います。 66 | 67 | それから元のOpenAIの論文に戻りますと、今言ったようなLSTMの比較みたいなLSTMにおけるスケール則みたいなことも、この論文でも検証されていまして、左側がモデル構造が違うんですね。 68 | Trasnformerの場合はスケール則が、パラメータ数が横軸になってますけどこういうふうになると、LSTMの場合には1層2層4層みたいにそれぞれスケール則を解くとこんなふうになりますよということで、Trasnformer以外のスケール則っていうのもあの検証をされている。深さについても検証してまして、これも元のモデルが何だったかちょっと忘れちゃったけど、確かLSTMだったような気がしますけど、層を変えたときにどういうふうな変化するかっていうのをこういった形でプロットするようなことがされてます。 69 | ポイントはTrasnformer以外でも別にあのスケールするっていうのは成立概念だということです。なんでこんなTrasnformerだけ注目されてるのかってのは後で話します。 70 | 71 | それからこれは補足に近いですけど、Trasnformerの中でよく知ってる人だと最近はMixture of Expertって呼ばれるモデルがよく使われているということを知ってる人も多いと思いますけど、このMixture of Expertにおいてもスケール則が存在するってことは言われています。 72 | 多分他にもあると思うけど代表的な論文で上が初期で下が最近で、なんかちょっとやり方でスケール則を検証してるものが下になります。例えばこんな感じで点線がうんTrasnformerで実線がこのMixture of Expertと呼ばれるモデルをスケールさせたときにどういう変化をするかというものを見ているものですけど、こういった形でMixture of Expertにおいても、スケール則が成立するとかなり綺麗な関係が成立するということが言われています。 73 | このMoE自体はちょっと今回の対象ではないのでMoEがどういうモデルかっていうのはDay8を楽しみにしておいてもらえばと思います。ここでポイントはrasnformer以外でも別にこのスケール則というのは、あの成立しているということです。 74 | 75 | 76 | それから基本的に今までの話は言語modelingなのでnextのtokenを予測するときの損失について縦軸としていましたけれど、それ以外にも他のドメインでもこのスケール則っていうのが整理するよっていうことも報告されています。 77 | これもOpenAIが出してた論文で、これ左がイメージのモデリングでテキストとイメージとかビデオとか数理計算とかimage2textとか、こういったものが他のドメインでも成立するよということも言われています。 78 | 79 | 80 | それからもう一つの論文でChinChilla Training Compute Optimalっていうものがあります。ここまでが1変数を制御している、横軸1変数にして、他の二つの変数については際限がないというか無限にあるという前提での経験則でしたけど、2変数を制限した場合の経験則っていうのも知られています。 81 | それが有名なのがこのTraining Compute Optimal Large Language Modelというものでしてそれが左側の実験結果、右側がPaLMと呼ばれるGoogleが出したモデルのバージョン2のPaLM2というモデルがありますけど、そのときにホワイトペーパーから取ってきたやつになってます。 82 | どっちも同じような図になってますけれど、これていうのはそれぞれの、例えば左側を見ると、6E-18とか1E-19と書いてますけど、この6E-18とかが使える計算量を意味してると思ってください。なので例えば左上のこの薄いやつ、薄い緑のやつは、それぞれ6E-18の計算量を使って、何かを変化させるというものです。 83 | その何かっていうのがこのパラメータになってまして、例えばこのなんかどこだろうこの線を見ると、いくつなんだろうわかんない100ミリオンとか、この線を見ると300ミリオンとかぐらいをパラメータに割り当てた場合、ということになってます。 84 | 当然パラメータを増やしたり減らしたりするだけだと、計算量っていうのは変わってしまうので、この場合は学習時間は固定していてtoken数を変更させているっていうなものになってます。なんでこの丸はそれぞれ学習時間とtoken数が変化しているというふうに思ってください。 85 | これをいろんな計算時間に対して見ていくと、どれも概ねこのUshapeというかの下側に凸な形になっていることがわかると思います。これ要は最適な値がありそうだということですね。ある計算量を考えたときに、めちゃめちゃ巨大なモデルを使うでもなく、めちゃめちゃ小さいモデルを使うのではなく、最適な値がありそうだということがわかると思います。 86 | 右側も同じような結果ですね、あの形は微妙に違いますけど大体同じようなことわかると思います。これがあのChinChillaの論文でして、これの使い方もまた後で話します。 87 | 88 | ここまでがあのスケール則とは何かのまとめでして、スケール則っていうのは毎回おさらいすると、計算資源とデータセット、パラメータと誤差の間にはこういった経験則がそうですよ。 89 | こういうべき乗則で書けますよっていうのがスケール則でした。両対数グラフで線形なるのは両対数をとってやるとわかるということも説明しました。それから一番有名なのはTrasnformerで本当のスケール則ですけど、それ以外のモデルでも成立しますし、言語以外のタスクでもスケール則ってのは確認されていますという話をしました。 90 | それから1変数を制御するのではなくて複数の変数を制御するような経験則も知られていて、有名なのではChinChillaと呼ばれる経験則があります。ChinChilla論文って呼ぶ方が正しいかもしれない。 91 | 92 | ここまでで計測スケール則の話をしたんですけどChinChillaのところで少しパラメータ数とデータセットサイズ、それぞれいじって計算量を固定しますよって話をしたのでちょっとその補足をしておきたいと思います。 93 | これよく出てくる式、あの経験則の近似式なんですけど、学習に必要な計算量ってどうやって計算してるんですかっていう話があると思います。これは「6*パラメータ数*token数」っていうふうに、あの計算されることが多いです。 94 | これ例えばGPTの場合だと175BillonがNに相当します。0.3テラtokenがDに相当する、それに6を掛けたもので3.14*E+23FLOPSというふうに計算できます。 95 | これ近似って書いてある通りこの6っていうのは近似なんですけど、これは概ねなんでこの6かっていうと、これ興味ある人だけでいいですけど、1層当たり、1パラメータあたりのMLP層における計算が6回あるということに起因しています。 96 | これアニメーションついてるんすけどアニメーションめっちゃ早いんですけど、基本的には1回MLPを計算するときに、フォワード方向で掛けて足すので2回、Backwardではそれが2回行われる、Backwardで入ってきたときに掛けて足すのと、自分が外に出す値を計算するときに掛けて足すすっていうので、4回あるので、大体1パラメーター当たり6回計算するというのでこの6という数字が使われています。 97 | これなんかよく謎の式として出てくるので少し補足しておきます。ちなみにこの近似って書いてある通りこれはあの雑な近似ではあります。というか下に「MLP層における」って書いてある通りなんですけどMLP以外の層も当然あるのでそれ以外の層では違う値が実際には厳密には必要になります。 98 | ただこれ系列長が短い場合だと、ちょっと僕も正確には詳しくないですが、MLPの計算量の方がAttentionより低い、一方で系列が長いとAttentionの計算で大きくなってしまうんですけど、そうじゃない場合基本的にMLPの計算力のがあの膨大なので大体無視できるでしょってことでこういった式が使われているそうです。 99 | 最近はどんどんtoken数伸びてきているので、若干無視できなくなっている傾向があるかなと思います。あと正確な式の例っていうのもありましてこの実装とかだとこういう何かちょっと例えばエンベディングでは2を掛ける。 100 | Attentionでは2×3×sequence_length×d_model×(key_size×num_head)みたいな、こういった感じで厳密にこういうふうに計算することができます。ただスケール則をやるときにはそのモデル同士の比較ができればいいので、多分そんなに気にしないで適当にこのFLOPSっていうのを計算してるんだと思います。 101 | はい。ちょっと補足でした。 102 | 103 | ちなみに補足がてらよく使うので見にくい図というか、考えてみてもらえばということでちょっと答え書いてないんですけど、さっきFLOPS大文字のSの概念と、あの計算量の話GPT3だと例えばこの3.14×10の23乗ありますよって話をしましたけど、これを使うと大体GPT3の学習にどれぐらい計算時間が必要か、A100が1000基あるといったときにどれぐらい必要かっていうのを見積もることができます。 104 | このFLOPSとかよくこういう計算にも使うので、興味ある人はちょっと計算してみてもらえばと思います。はい。今やらなくて大丈夫、割ればいいだけです。ちなみに言語モデル開発しようと思うと、めちゃめちゃよく使う意識でして、WebLab_10Billonとか開発したときは裏でこういう式とかめちゃめちゃ飛び交ってました。 105 | 106 | 107 | これは裏話でここまでスケールとは何かについて話しましたがこれは何かっていうのは一旦理解できたと。そういうべき乗の関係にあって両対数すると線形になるんですね。1変数じゃなくて2変数の経験則ってのもあるんですけど、そういったことは理解できたと思いますけれど、それどうやって使うんですかっていう話を次していきたいと思います。 108 | 109 | 110 | これ同じですねこのべき場の関係が成立しますよというような図です。これがスケール則でした。これどうやって使うんですかっていう説明をするにあたってちょっと使われてる例をいくつかピックアップしました。これGPT-4のテクニカルレポートから取ってきた図ですけれどこの緑が実際のGPT4のパラメータ数だというふうに言われていますこれが1になってます。 111 | これに対してGPT4では、それより1000分の1ぐらいちっちゃいモデルでのスケール則を測って、推論した結果、これぐらいの性能になるだろうっていうのをプロットして作ったっていうふうに書かれています。要はこれGPT-4例えばいくつかわかんないっていう現実はわかんないすけど、このパラメータを訓練する前にそれより小さいモデルでスケール則を検証して、これぐらいいくんですねと、これしっかりちゃんと性能上がるんですね。 112 | ちょっと確認したというふうに言われてます。こういった形で自分がモデルを作ろうと思ったときに、モデルを大きくすることに意味があるのかっていうのを見積もることができるようになるわけですよね。これが使い方の一つですね。 113 | ちなみにこれ公開されてないときにちょっと計算してたんですけど、リーク情報出る前にちょっと計算してるんすけどこれ1で何か左端に100ピココンピュートっていうのが書いてあって、多分普通に計算すると、10の10乗より大きくなって、これが1だとしたときにもGPT推定は10Bになるので、100ピコが例えば10の3乗ぐらいのTrasnformerだとしたら少なくとも1Trillionにならなきゃいけないってことで、何かこれで大体推論できるなと思ってました。 114 | 115 | その他の使い方としてモデルを、どちらがモデルが良いかっていうのを比較することもできます。これどちらのモデルが良いのかって比較しなければ別に同じパラメータで比較すればいいんじゃないのって思うかもしれないんですけど、もうちょっと厳密に言うと、モデルをスケールさせるとしたらどっちのモデルがいいですかっていうように予測することができるように左側のこの二つの図は最初の前半でも少し話しましたけれど、左側がモデル構造を比較して、右側からパラメータを比較してるものですけど、左側だと例えばTrasnformerの方が、どうやらこれスケールさせてってもずっとよさそうだということがわかります。 116 | LSTMをスケールさせていってもTrasnformerを逆転することは、どうやらなさそうであるということが何となくわかるかなというふうに思います。右側の例だと層を変えてますけど例えば10^5ぐらいしかパラメータがないときに比較すると、これどんなモデルが良いかわからない、どの層がいいかよくわかんないと思うんすけど、このスケール則を測ってると、どうやらパラメータ数を増やしていける前提だったら6層以上に層を増やしていった方が良さそうだということが予想が立つわけです。 117 | このポイントは実際にこれを計算する前にフィッティングしてやればちょっとさっきまでわかるということですね。GPT4の場合と一緒ですけどこれGPT4を実際作る前にこの曲線を引くことができるので、そういう形で小さいパラメータをつくっていろんなパラメータを検証してあって、大きいパラメータでどれぐらい、どういう関係になるかってのを予測することができるっていうのが一つの重要なポイントです。 118 | こうしたやり方はあの研究でもよく使われてまして、これもあのMambaという、研究自体はちょっと紹介しないですけど、あのこのMamba呼ばれる論文から引っ張ってきた図になってます。これ横軸いろいろ書いてるんすけどそれぞれ何か違うモデルですね。 119 | HyenaとかRWKVとか、TrasnformerとかResNetとかH3++って感じで、これがなんかTrasnformerじゃないやつらで試して作られてるものなんですけど、何が言いたいかというと論文でも実際このスケールをさせたときに、どういうふうになりそうかとスケールさせたときに、この提案手法は勝てるのかっていうのが、実際研究されてたりもします。 120 | ちなみにこの論文ちょっと若干余談なんですけどTrasnformerとTrasnformer++っていうのがありまして、これ確かTrasnformer++はLlamaで使われる構造で、Trasnformerが元のGPTの構造なんですけど、これ見ると結構スケール則が違うということもわかると思います。薄いオレンジとオレンジで。なので結構構造が大事だよということも実はこの図でわかったりします。 121 | あとはMambaっていうのがこの紫ってなんか強そうなんですけど、これが何かっていうのは多分Day8でやるんじゃないかなと思ってこれも楽しみにしてもらえばと。 122 | 123 | それからちょっと似てる話ですけど効率性を、効率的にやろうと思ったときにどうすればいいのかっていうのを知ることもできます。 124 | 左側が横軸がtoken数、縦軸がLoss、色がパラメータに対応しています。これ見ると、例えばこれ若干直感に反することを言ってるところあるんですけど、あるパラメータを固定したときには、基本的にtoken数固定したときには、大きいパラメータのモデルが、サンプル効率がいいロスが下がりやすいということがこの結果からわかったりします。 125 | それから逆に右側が横軸がコンピュートになっていて、色がモデルサイズであることは変わらないんですけどこれを見ると例えば10のマイナス3乗の計算量があるときには、これぐらいのコンピュータを使えばいいということがわかったりします。 126 | これ別に大きければいいというわけではないと。この辺の理屈は小さなモデルだと、学習がロスが下がらなくなるというのでちょっとあのスケール則を書いてるときに、なんか1個1個のプロットを説明したと同じような話ですけど、あの計算量が与えられたときに、どうやら、別にパラメータを増やせばいいわけではないいうことはわかりこの計算量が与えられたときにどれぐらいのバジェット、どれぐらいのパラメータ数とtoken数に割り振ればいいのかっていうのを、あの計算しようとしたのが、先ほど出したこの2変数の関係っていうふうに言ったChinChillaと呼ばれる経験則になってます。 127 | 128 | 129 | ChinChillaはモデルの名前なんですけどなんかChinChilla則って言われたり、ChinChillaケースって言われたりするので、何かその辺を丸ごと足してChinChillaというふうに、大体呼ばれてると思えばと思います。 130 | 左側の図は先ほど見せたのと同じで、それぞれの色が計算量に相当してまして、パラメータ数を変更させた場合です。右側の図が増えてるんですけど、これを各FLOPSで最適なパラメータに直したものっていうのが、この中央でこれを同じようにデータ(token数)に対して、直したものが中央になります。 131 | 例えばこれパラメータ見ると何か例えば3E-21を使えるんだったら、あのこの一番下のやつをピックアップしたやつか、この右側の真ん中の方に行ってきていて、同じように1E-12の場合はぴって引っ張ってくるみたいな、やったときにどういう関係があるかっていうので、これを見てみると何となく大体線形っぽい関係にわかります。 132 | 右側がtokenの場合の同様の例ですね。これをフィッティングしてると、例えばこれ適当な値ですけど10^24よりちょっと低いぐらいの計算量が使えますよっていうときには、パラメータ数は63Billonにすればいいと。同じところが、これ取られてるんすけど、データ数がどれぐらいすればいいかっていうと、1.4Trillion tokenにすればいいということがわかります。テラtokenですねごめんなさい。 133 | こうして作られたのがChinChillaと呼ばれるモデルになっています。これあのGopherっていうモデルがありまして、GopherがDeepMindがこの前に出してたモデルで、これが280Billonでtoken数が0.3テラtokenというふうになってます。 134 | 要はこいつと比べるとこのChinChillaっていうのはモデルサイズがちっちゃいんだけど、あのトレーニングtokenを増やしたと、それはどうやって決まったかというと先ほど言った通りですけど経験則に基づいてどのぐらいのバジェットをパラメータに割り振ってどれぐらいのバジェットをtokenに割り振るかっていうのを、この経験則によって決めた値を使ってやってやるということをしているものです。 135 | 結果としてはこれで多くのケースより巨大なモデルに勝てるということが実験上示されています。これ左側のやつが実験上の結果じゃないんで、ちょっと実験結果飲みたい人はこの元の論文を見てもらえれば良いと思いますけど、あの巨大なモデルにかかってるということでこの関係性が良さそうだということが示されています。 136 | ちなみにこれも余談ですけどこのの求め方このChinChilla則ってのは実は何かいくつかの方法で求められてまして、それぞれ大体同じような経験則が出るってことが知られています。この関係式っていうのがよくこれも知られてまして、大体最適なtoken数っていうのが、パラメータ数に20をかけたもの。 137 | これと同じですね。70Billonを訓練するのに1.4とBillonのtokenを使うということになってますのでこの式の20っていうのも謎のマジックナンバーとしてよく出てくるので覚えておくといいと思います。 138 | 先ほどPaLM2の図を見せましたけど。PaLM2でも同じような経験則が成立しますよということが言われています。 139 | 140 | ここまで見るともうこのtoken数だけであのパラメータ数を、あるパラメータ数に対してtoken数を決めればいいあるいはtoken数が固定データセットサイズが限られてんだったら、それに対してあるパラメータ数で決めてしまえばいいんじゃないかっていうふうに思うかもしれないけど、これはそんなに簡単ではないということも調べています。 141 | 何のことですかというか今最適な割り振りっていうふうに言ってたじゃないかというふうに思うかもしれないすけど、一つの観点は、さっき話したのは、訓練のバジェットだけを考えていたという点が、現実的ではあまりないということがあります。これ左がのが訓練時のFLOPSをどうするかっていう話で、ChinChilla Optimalで作ったやつ13Billonのモデルが例えばこんな感じのスケールをしたと。もうちょっとちっちゃいモデルを大量のデータで学習させたモデルは、あのこの黒い黒というか、紺色ですかね、みたいな線だとする。 142 | これロスが同じような値を取るモデルっていうのをピックアップすることができて、この状況で比べるとやっぱり訓練のFLOPSは13Bの方がいいと、ChinChillaOptimalな作り方をする方がいいということが左の図からはわかるんですけど、今度はこの推論するということを考えたのがこの真ん中の図にあります。 143 | これ当然なんですけど、実際にはこのモデル学習した後に、皆さんGPTとかChatGPTとか使ってると思いますけど、みんなが使うときにも計算量がかかるわけです。この推論のときに使うかかるコストっていうのは当然ちっちゃいモデルの方が、小さい、さっき推論のコストが2回分とか2×NDだっていうふうになんかちらっと言いましたけど、それからするとこのコストが推論時にかかり続けるので、学習のコストだけじゃなくて色も考えると、あの7Billonの方が常にちっちゃいわけです。 144 | これが例えばこのぐらいのtokenだったときは、このぐらいのサイズのときに、これ要はゆくゆくは絶対推論コストが低い方が逆転するんですよね。この右側足したやつですけど、そうすると、推論時のコストまで考えてやっていいモデルを作った方がいいんじゃないかっていうのが、あの考えてる人もいたりします。 145 | これがあのBeyond ChinChilla-Optimalというな研究になってる。この論文の結果を一つだけピックアップしましたけど、右上に書いてある数式は6ND_trっていうのが、学習時のtokenに対して6Nさっきのマジックナンバーを掛けたもの。D_infってのがinference時のもので、これに2をかけてるのはバックワードがないので、4回分の計算はないからですね。左の図がなんか綺麗な図が書いてありますけど、Inference tokenを増やしていったときに、ChinChilla係数に対して何倍にしていくかっていうのを、この色が表してます。 146 | これも当たり前なんですけど推論回数が多くなるほど、ライフタイム全体では、あの学習token増やすと要はケースを大きい方にする方が、あの、あるロスを見たときに、あの一番よくないということがわかります。あるtoken数を見た時に一番良いっていうのは変わっていくよっていうことがわかると思います。 147 | 148 | こういう考え方もあるねっていう話をしましたけど、これはLlamaとかでも、実際token数を増やすことが多いということにも多分繋がってるっていうふうに言われています。ちょっとさっきの図を持っていましたGopherというのが、DeepMindが元々作ってた巨大なパラメータを少なめのデータセットで学習したものになってまして、ChinChillaっていうのがさっき言った経験則によって学習したものになってます。 149 | これがあの係数が20このtokenをパラメータで割った値が20。20倍のtokenを利用しましょうということでしたけど、例えばLlama2だと学び7Billonでも70Billonと同じtokenを使ってますけど、7Billonのものに関しては1.8Trillionのtoken使ってるので、285倍の係数と全然違う値を使ってる。70Billonは28.5倍。Llama3に関しても、70Billonが214倍、400Billonの方が37倍ということで、このChinChilla-Optimalが実際に巨大なモデル、巨大なtokenの学習で使っているケースもよくあります。 150 | 151 | 152 | ここまでのまとめが、スケール則ををどう活用するかって話をしてきました。使い方としては投資するかどうか、大きいモデルを開発するかっていうのを割と予測できるというような使い方もありますし、これが実際GPT4の開発にも繋がったというふうに言われています。 153 | モデル選択もできるとパラメータを増やしたときに、どっちが良いモデルかっていうのを実際にパラメータを試すことなく、そのモデルの巨大なモデルを作ることなく検証できるってのがいいとこだと。それからどれぐらい計算資源を割り当てるかっていうのでChinChillaを決めるっていうものがあり、そのChinChillaOptimal以外にも推論コストを考えたときの最適なtokenを議論する研究もなされています。 154 | 155 | 156 | ここまでがスケール則の活用法でしたけど、少しだけ補足をいくつかしておきたいと思います。この予測可能だというふうに言いました。スケール則ってのは先ほど言ったように、あのべき乗の関係で綺麗なフィッティングができるので、ある意味予測可能なわけです。だけど同時にこのサプライズというか予測不可能な部分っていうのもあるっていうふうにも言われています。これがこれAnthropicっていうOpenAIから独立したというか枝わかれした会社がありますけど、その会社が出してる論文で「Predictability and Surprise in Generative Models」って論文で触れられていますけど、この予測不可能だっていうのがEmergent Abilityと呼ばれるなあの現象になっています。 157 | これ初回で話した通りですけど、例えばパラメータを増やしていったときに、元の計算が突然できるとかそういったものがありますというのが知られています。これ初回でフェアのために何か本当にそうなのかみたいな話を少し言いましたけど、これは実はミラージュ、幻覚なんじゃないかっていうなの研究も出ています。 158 | 何を言ってるかっていうといろいろな反証をしてるんですけど、これ要は性能の測り方に寄るでしょうということを言っています。なんか横軸が対数になっていて、うんそもそも何がそのEmergentって言ってるけど、何が創発なんすかみたいなそういった議論もありますけど、少なくとも何かこういう急に何かできるようになるように見えるということが、起こるという期待もこういった言語モデル開発を加速させる要因の一つなのかなというふうに思います。 159 | 160 | 161 | それからこれもあんまり本題と関係ないですけど面白い現象としてブGrokkingと呼ばれるものを存在しています。これ言語モデル自体ではないんですけど、Grokkingっていうのはめっちゃ過学習させたモデルをずっと学習させ続けると、突然テスト性能があるというような現象になってます。 162 | これはなんかa○bって書いてますけど、こういう2項演算の関係を表してて、aとaだったらaとかこういう何か表があってこの穴埋め問題を解くというようなタスクになってます。この赤線がこの訓練の性能で、緑があのテスト性能なんですけど、横軸これオプティマイズのステップになっていて、なんか10^2とかから10^5とか10のN乗のスケールなんですけど、なんか最初めちゃめちゃ解けないのに、過学習させた状態での学習を続けると性能が上がるというふうに報告されています。 163 | これもある意味予測不能なわけですね計算量を増やし続けると、不思議ことが起こるという現象として知られています。これ何が起こってるのかみたいな研究としてはホットトピックの一つとして知られていて、これなんかrepresentationの学習だっていう、良いrepresentationが学習されると、汎化が起こるんだと。学習し続けるとこういう表現なるんだという研究もありますし、何がこういう良い表現を獲得してるっていうことに繋がってるのかみたいなことも研究されていたりもします。 164 | 個人的には面白い時かなと思ったんすけど、今日の話は少し関係ないのでこのぐらいにしておきます。 165 | 166 | それから予測不可能な改善という話とも関係するんすけど、実際にはロスを下げたいというよりは何かのダウンストリームの性能を上げたいことが多いと思います。 167 | そういう意味で下流の性能を見た研究もありまして、これは学習以外のデータに対してのテストロスがどうなるかっていうのを見ています。だから仮タスクというよりはデータ分布が変わったときですね。これ見ると大体オフセットが違う実際のロスは違うんですけど、傾きはどうやらスケールしてとかっていうことがわかると、それからこれもコンピュートを指定したときですね横軸にとったときにパラメータはどのぐらいのパラメータだと一番いいかっていうので、これも大体綺麗な関係があるかわかると思います。 168 | 一方でこういう軽いタスクが絶対性能が上がるのかっていうとEmergentアビリティの話とだいぶ関係がだいぶ違うこと言ってると思うんですけど、実際にはこのいろんな関係があるっていうことも、検証されています。これ多分GPT4の論文でも議論されているものですし、このGPT3の論文でも言われてるものですけど、綺麗に上がるようなタスクもあれば、急に上がるものもあれば全然上がらないものもあるということで、実際には必ず上がるわけじゃないっていうのは注意してもらえばと思います。 169 | それからこういう何かInverse scaling lawっていうのも知られていて急に悪くなるような、あのタスクも存在するっていうふうに言われていて、去年とかだと何かInverse scaling Prizeっていうことで、スケールさせるほど精度が悪くなるタスクを見つけて賞金が出るみたいな、そういうコンテストも開いていたりします。 170 | 171 | 172 | 具体的な求め方についても話します。 173 | さっきからチラチラ言ってた通りなんすけど基本的にこれどう図るかっていうと、基本的にはいくつかの条件で実験してフィッティングするって言ってんのは、すごい単純に言ってしまうとそうなります。左側GPT4の論文から取ってきた図で説明したもんですけど、グレーのやつを例えば実験してみて、これぐらいのロスになるんだなっていうので、フィッティングするとこういうカーブになります。 174 | ちなみにこれ、なんでこれ直線にならないんだっていうのをすぐ説明しなかったですがこれ縦軸が実は普通のロスと違ってBits-per-wordっていうのになってて、多分2乗スケールのロスになってるからだと思います。 175 | 右側も同じですね。この各点について何かいろんな設定で実験してやって、それを結果を見るということをしてますけどよくよく考えるとスケールさせるときにモデルサイズどうすればいいんでしたっけとか、何をどういじるとモデルサイズが大きくなるんでしたっけ、どういうふうに言えばいいんでしたっけとかですね。 176 | あのモデルサイズ変えたときにハイパーパラメータってどうすんでしたっけそういった細かい問題が出てくる。最初の方ですけどモデルサイズどう変化させるかっていうので、前回やった、こういう図があると思いますけどモデルサイズ変えようと思ったら別にパラメータ、層の数を増やしても、いいわけですし、この埋め込みの次元各tokenの次元を増やしてもいいわけですし、各随所に出てくるこのフィードフォワードネットワークっていうのの中間層の次元を上げてもいいですしヘッドを増やしてもそういうのあのパラメータ自体は上がるということで、これどれをどのぐらいやるんですかっていうのが細かく考えると重要になってきます。 177 | この辺は元の論文でも一応議論されてまして、これ三つほど出してるんすけど例えば真ん中のがアスペクト比っていう、モデルのエンベディングのサイズですね。dモデルっていうものを層数で割ったもの、アスペクト比という縦横比みたいなもので幅と深さの比率をアスペクト比っていうふうにこの論文では呼んでいますけど。こういったものを変えて実験してみたっていうのが最初の最初じゃないOpenAIのScaling Lawで話されていました。基本的にはこの辺見るとなんかあんまり性能に影響ないっていうふうにこの論文では言ってますけど、この辺を気にしながらモデルスケールすることが多いです。 178 | 気にしながらっていうのの実例を出した方がわかりやすいと思うので、実際にこれ開発者じゃないので、あの結果を見て推論してるだけなんで嘘ついてるかもしれないですけど例えばLlama3の論文を持ってくると8Billon,70Billon,405Billonで層の数、モデルDimension、埋め込みの数次元ですね、フィードフォワードの次元、アテンションの数っていうのを、こういうふうにしたよっていうふうに言われてます。 179 | これさっき言ったアスペクト比、縦横比がこのモデルdimentionをLayerで割ったものなんで、これそれぞれ見ると128,102.4,130ってことでこれ大体100から130ぐらい、なんかおおむね同じような値になっていることがわかると思います。 180 | それからモデルとフィードフォワードの次元数ですね、モデル次元数に対しフィードフォワードの次元数は3.5倍になっているということがわかります。これ約3.5かな。ちょっと自信ないですちょっとちゃんと計算したとかいった計算したら、ちょっと違ってたら教えてほしいんすけど大体3.5倍ぐらいあったとアテンションのヘッドはこのFFNの次元数と同様にスケールしたモデルの次元と同様にスケールしているということがわかる。 181 | こういった感じで幅とかを大体同じような係数で、なるべく伸ばしてくと、ただこれ、指定したパラメータ数にしようと思ったときに、当然どっかは完全には固定できないので、若干変わりますけど大体同じような比率でスケールさせているというようなことがわかると思います。 182 | 183 | 184 | それからこれCerebrasGPTっていう別の論文ですけどこれの論文を見るとこれちょっとさっきの時Llamaよりだいぶちっちゃいですけど、アスペクト比が大体上から76.8,77.7,85.3,85.3みたいな感じで大体これも同じような値になってて、モデルとFFNの次元がそれぞれ決まっていると、4.0となってて、ヘッドはちょっとこれも論文だとだいぶ規則変化してますけど、大体この辺を揃えているということが経験上わかるかなと。もう一つの話がハイパラをどう変化させるかで、これも同じCerebrasGPTというのを見てみると、例えばこのLR Decayのタイプですね、学習時にDecayをどうするかで、ちっちゃいモデルとlenearにしてんだけど、1.3と2.7はなぜかCosineで6.7はLinearで13BillonはCosineみたいな謎のことをしていることがわかると思う。学習率を見てみると、6.0E-04(6.0掛ける10の-4乗)だったのが1.2*10^-4になってたりこの辺が変化してるということがわかると思います。 185 | これなんでかっていうと、学習したことは方だったら得られたのと、言語モデルに限らずだと思いますけど、あのモデルのサイズとかによって結構この辺の最適なパラメータ数ってのは変わるっていうのは何となくわかるかなと思ってます。 186 | 左が何かある論文から持ってきた幅を変化させたときの学習率がどのぐらいだといいのか、最適なのかってのをプロットしたものになってます。色が幅に対応していて、横軸が学習率をlogにしたものですね。これ見ると例えば8192の場合だと、このマイナス16ぐらいにいいとこいますし、128の場合だとマイナス10ぐらいということです。 187 | 要は経験則としてモデルサイズを大きくしたとき、大きくしたときには学習率をちっちゃくするってのが大体だということがわかると思います。CerebrasGPTもそうなってます。 188 | 189 | バッチサイズを大きくするといいってのも経験則としては言われている。この図はちょっと関係ないですけど、そういう形で実際にはそのパラメータをスケールさせたりする必要があるということに注意してください。ちなみにこれある論文って言ったんすけどμTrasferっていう論文でして、この論文は実はこのパラメータを変える必要があるってことを主張してるわけじゃなくて、何かいい感じの方法を使うといい感じってだいぶ大雑把に言いましたけど、この幅によらず最適なこの学習率の値バッチサイズもそうなんすけど、同じできるよってことを主張してる研究だったりします。 190 | ちょっと面白いので説明したいとこなんすけど興味ある人は、論文見てもらえばと思います。何してるかっていうと簡単に言うと重みの書記官ときとかに、入力の数とかに応じて初期化を変えると思うんですけど、それに似たことを学習率とか、出力のweghtにして掛けてやるとするとこれがweightに対して最適な学習率っていうのがこのケースだけであの最適になるということを示している研究だったりします。 191 | ちょっと興味ある人は読んでもらえばと思い、これμTransferの何か活用方法だけここに行ってますけど、これμPって書いてあるのが、このさっき言った怪しい怪しくはないんですけどただいい、いい感じの初期化をするということをしたものでして、それやると学習率が変わらなくても、いいよってことがCerebrasGPTの場合だと言われています。 192 | ちなみにLlamaの場合はなんかちょっと論文見たんですけどちょっと厳密によくわかんなくてなんか参照したよっていうふうに書いてあるんすけど実際learning rateとは何かちょっといじってるみたいなので、この辺どうやってるかっていうの多分論文とかにあると思うのでちょっとモデルを実際興味ある人は見てもらえばと思います。 193 | 194 | 195 | トレーニングに関するスケール則の話がここまでで終わりです。基本フィッティングすればいいんですけどモデルサイズをスケールさせるときにはハイパラをどうするかっていうところが、あの結構実験的にやられてまして、モデルサイズについては大体なんか幅アスペクトratioみたいなもの、幅と深さの比率みたいなものを維持しながら受けさせますし、学習率とかはスケジュール、徐々に小さくする、大きくしたときに徐々に着するとか、例としてμTrasnferという技術を使ってたりするということをご理解いただければと思います。 196 | 197 | 198 | 講義に戻ります。ちょっと演習の時間もあるのであと20分ぐらいで駆け足になりますけど、最後最近のスケールトレンドってことで「推論時のスケーリング」についての話をして終わろうと思います。モチベーションから話すと、ちょっと頭で考えてみてもらえれば一瞬でわかると思うとんですけど、「バナナの色は何ですかって言われたとき」と、今日の講義聞いた上で、「スケール則の問題は何だと思いますか」って聞かれたとき、多分あの考えることが違うと思うんですね。 199 | 「バナナの色なんですか」っていうと黄色ですね。もしかしたら緑かもしれないけど、物によるかなみたいな、おもちゃだったら違うかもみたいな、だんだんあの、考えていくといろいろ出てくるかもしれないすけど、少なくとも「スケール則の問題なんだと思いますか」って聞かれたときに、今日の話からするとスケール則っていうのはこういうものだから「どうだろう」「この辺が問題かな」みたいな考えとはやっぱ思考としては違うってことは何となく思うかなと思います。 200 | なんか人間的にはこの二つって全然違うしあの、答えるのに必要な考え方っていうのも違うように思えるわけです。推論時のスケールって言ってるのはこういった形で、あの簡単なものについては簡単に答えてもいいですし、そうじゃなくて、深く考えなきゃいけない問題に対しては、考える時間に計算資源を使うというふうにしたときに、これいいことがあるのかっていうような話になってます。 201 | これの仕組みは言語モデルでも効果的ですかっていう話と、これをどう実現できるかっていう、こういう二つの話が最近のトレンドとして出てきています。効果的ですかっていうのが、最近o1と呼ばれるモデルがOpenAIから出ました。プレビューとして出てますけどこのo1で注目されています。これあのo1の論文ってかブログにある図で、左側が訓練時の計算資源をスケールさせたときに、AIMEというロジックのベンチマークがあるんですけど、accuracyがどうなったかというと、何となくスケールしてる。右側がtest-time computeっていうふうに書いてると思うんすけど、推論時に計算資源を増やしたときあるモデルを使うんだけど、簡単に答える方法と深く考えて答える方法みたいでだんだん計算資源を増やしていったときに、性能がどう変わるかっていうので、これもスケールしていってるということがわかると思います。 202 | こういった形で、要は考える時間をどうやら推論時に使うと、つまり計算資源を推論時に使うのはいいことがありそうだということがわかります。 203 | 204 | 205 | そうすると次の話はどうやって計算資源をスケールするのか計算量スケールするのかって話ですけど実は一番簡単な方法はいくつかもうこの講義でも触れていて一つは例えばChain of Thoughtを使うっていうのもその一つです。 206 | Chain of Thoughtを使うと出力するtoken数が増えますよね。その思考の過程を自分で出力するので、これってのはある意味計算資源を増やして使っちゃってると。フェアに言うと、直接答えるのと、tokenをいっぱい出して答えるのは違うわけですよね。ありていに言うとかかるお金が違うわけですからそれからMany Shot ICLっていうのも、あのDay2で多分少し話したのかなと思いますけどIn-Context LearningのときにIn-Contextとして入れる集合を、サイズを増やしていくとどうなるかっていうので、これもスケールしますよということが言われてましたけど、こういった形で簡単にこのPromptingのときに出力するtokenだったり、入力するtokenっていうのをいじってやると、計算量を増やすことができるので、なんでこの辺の研究から見ても明らかなんですけど、基本的にうまく考えるのに計算資源が使えれば性能が上がりますよってのは、既にDay3までにも話してた話だと思います。 207 | 208 | 209 | それからDay3でもDecodingっていう仕組みを、あの話したと思います。このDecodingにもいろんな方法があってGreedy Decodingだと単純に一番いいやつを選んでいく、一番確率が高いやつ選んでいくので、すごい単純ですけど、こういうトップPを取るとかトップKを取るとかして、最後に一番いいやつを選ぶみたいなことをすると、これも結局計算をたくさんしてることになるわけですね。1個選ぶわけじゃないので、次に評価しなきゃいけないものがどんどん増えていくわけなので、こういった形で増やすっていうのも一つのやり方として存在しています。これ何かDecoding方法の一覧ですけどこれ興味ある人いたら言ってください。 210 | 211 | 212 | どうしようかな。時間も関係あるんでなんか最近の例を一つだけ紹介するとContrastive Decodingというのは例えばいまして、Extrinsicって書いてあるこの表中の軸に相当する方法の一つ、外部のモデル別のモデルを使う方法なんですけどこの研究によると、単純に自分が自分自身のこの出力の確率を使うよりも、なんかしょぼい言語モデルの確率を割ってやったものを使った方が、厳密にはLogの場合には引いたものですね。この方が良い指標になっているということが知られています。これなんか多分僕、なんかよく話しすぎちゃうものやよく出てくるものじゃないものについてエキスパートモデルが高く値を出していた場合、例えば「1961」ってのは普通はあまり出さないわけですけど、しょぼいモデルも出さないんだったら、これをベースラインとして割ってやる(引いてやる)と良いよっていうようなDecoding方法も出てきています。ここでContrastive Decodingの詳細を理解してほしいということよりは、Decodingのときにその他のモデルを使って性能を上げると、要はこのアマチュア(しょぼい)モデルの計算がさらに増えるわけですけど、こういった形の概念として新しい手法も出てきていたりしますということです。 213 | 214 | 215 | それからここまでのDecodingの話は基本的に次のtokenをどう選ぶかという話をしてましたけど、Decoding、次のtokenをどう決めるかだけじゃなくて、全体(プロセス全体)としてどういうふうな生成を行うかっていうのを一段広く、上から見ましょうというのでMeta Generationという言い方をするような研究も出てきています。このFrom Decoding Meta Generationというのが、6月かな、に出たサーベイでして、推論時の言語モデルで使うアルゴリズムを広くサーベイしたものになってます。この2章がMeta Generationて概念になってますけどちょっとこれがどういうものかについて説明していきたいと思います。そうね三つぐらい種類があるっていうふうに書かれてます。Parallel Search, Step Level Search, Refinementですけど、ちょっとこれ何か説明より具体を見た方がわかりやすいと思うんで、それぞれ具体を説明していきたいと思います。最後にこれを図を振り返ってこういうもんだなと思ってもらえばいいと思います。 216 | Parallel Searchの方法の一番代表的なのがBest of Nと呼ばれるものです。これ要は、これをtokenだと今までの話と同じなんすけど文を何個か生成してみて一番いいやつを選びましょうっていうのが、このParallel Seacrhの一番簡単なBest of Nっていう方法ですね。一番いいのっていうのがいろいろ考えられて、LLMのスコアを使うっていうのが、これがBeam Searchとかそういう普通のBeam Searchとかに相当するものですけど、valifierと呼ばれるような学習した評価機を使うような研究だったり、GLUEとか機械翻訳のときにGLUEとか、特定の指標を使うみたいに、何かの外部の指標を使って、とりあえずN個生成して、一番いいやつを、後で選びましょうとそういうアプローチになってます。ちょっとMBR Decodingの話しようかと思うんすけど、時間がだいぶ迫ってるかもしれないので簡単に言うと、これがMBR Decodingというのが最近機械翻訳で注目されてる一つの方法らしいんですけど、この例の中で言うとGLUEとかの指標を使っていいものを選ぶというような研究の例になってます。そうですね今言った通りですけどこの何か期待値の中にUて書いてあるけど、このUっていうのが、このさっきこのスライドでのスコアに相当してましてGLUEとかMeteorとか、いろんなものを使っています。これは実際にこれ人間のサンプルを用いたこういう関数が必要なんですけど、これをこのMBRだとやめていてHumanの代わりにモデルから生成したものをサンプリングして期待値を取るみたいなことをやるのがMBR Decodingというやれるやり方になってるんすけど、ちょっと詳細が知りたい人は、だいぶわかりやすい資料が上がってましたのでこのURLから飛んでもらえばと思います。ここではこの、要はスコアを選んでいいものを選ぶというようなやり方でいろいろ発展してきてるんだということを理解していただければいいと思います。 217 | 218 | 219 | それからBest of Nとはちょっと違う方法として、N個を生成した後に、それらを集約するという意味では、Day2でやったSelf-Consistencyをこの枠組みの一つとして説明されます。Self-Consistencyは下のようなもんですけど推論のCoTを応用したものになってて、言語モデルにCoTでのいろんなReasoning Pathを出させて、"Marginalize out reasoning paths"って書いてあるけどそのreasoning pathを出した後にこの一番よく出てきた答えっていうのを最終的に答えとするというようなやり方になってると思います。 220 | これもN個の中から1個選んでるわけじゃないんですけどN個出力して、それらを集約する形でこのN個はそれぞれ独立に動く(パラレルに動く)ので、さらに動かして最後集約するという意味でこのParallel Searchの枠組みの一つの代表的な手法になってます。このアグリゲーションどういうふうにこの結果を集約するかだったり、どういうふうにスコアをつけるかっていうので、いろんな手法が知られてましてこれもさっきのサーベイ論文に入っているので興味を引いていけばてもらえればと思います。 221 | 222 | 223 | ここまでパラレルサーチの中でMajority VotingとBest of Nっていうのが出てきましたけど、これあるタスクこれ確か数学のタスクだと思うんすけどタスクが載ってないんで興味ある人はこの論文見てもらえばと思うんすけど、例えば比較すると、普通にMajority Votingするのがこの黒線で、あの青線がこのBest of N、ORMって書いてあるけど、これOutcome-supervised Reward Modelってもので、要は全体に対して、これが正しいか正しくないかを推定するリワードモデル、結果をすいてするReward Modelってのを用意してあって、Reward Modelにとって一番良かったやつを最後選ぶっていうそういうやり方になってます。Majority VotingはN個出して最大のものを選ぶっていうやり方です。これやると比べると、リワードモデル使った方がいいよと言われています。一応補足なんですがMajority Votingは別にそのリワードモデルとか作る必要ないので簡単な方法であるっていう利点はあって、best of Nの方のORMっていうのは、リワードモデルを別で学習しなきゃいけないってのが欠点としてあることは一応補足しておきます。 224 | 225 | 226 | それからこのオレンジの線をスルーして話したんですけど実際にはこの論文はこのオレンジのやり方を提案してる論文になってます。それがこのPRMっていうものでして、PRMってのはProcess-Supervised Reward Modelっていうふうに呼ばれています。あるいは論文によってはProcess Reward Modelって普通に読んでるものもあります。これは全体に対して合ってる間違ってるっていうのを予測するんじゃなくて、このプロセスごとに合ってる間違ってるみたいのを予測するモデルを作るというものです。これあの数学の問題みたいなプロセスがわかりやすいですけど、最初に"Let's call the numerator x"みたいな感じでそれぞれのパスが正しいか正しくないかみたいな、これはユーザーが提出してる例ですけど、ユーザーの提出した例を使ってプロセスの正しさを予測してるということをしています。これをやるとさらに性能が上がるっていうことが左の図からわかりまして、これちゃんと説明しなかったですけど横軸がN個なんで生成するものの数になってますけど、これ増やしていくとさらに性能が上がるということがわかります。このPRMみたいなプロセスに対して評価を付けるっていうな説明をしましたけど、こういったやり方をするのがStep Level Searchっていうふうにさっきのサーベイ論文にまとめられています。ごこの一つ前の研究ではこのProcess Reward ModelのRewardを使って、あのBest of Nを選ぶってやり方をしましたけど、Best of N以外にも、このプロセスごとに塊を作って生成していってビームサーチみたいなことをすることもできます。要は最初はこの2個が良さそうだからこの2個を生き残らせて、こいつからまた発生させて2個作ってまた次のステップ3の選んでみたいな。これを繰り返すっていう方法です。これ丸が一つのプロセス、tokenじゃなくてプロセスに対応してましてというのが普通のBeam Searchの違いですけどそういった研究もあります。ちなみにそういった研究もありますって言いましたけどこのTree of SearchっていうのをDay2で多分やったと思うんすけどこれがほぼ似たようなことをしています。みたいなことっていうのはこのリワードモデルっていうの使う代わりに言語モデルで、この状態がいいものか、こっからこれGame of 24の例ですけど、足して24になる数字の作り方をしてくださいっていうときに、あのもう絶対に達成できない場合は、あの言語モデルがFailとするというようなことをしているものですけど、そういった形でやってるっていうのも、言語モデルをあのリワードの評価として使っているステップレベルサーチの例だと思っていただければと思います。これもさっきと同様ですけど探索方法とか検証するステップの違いですね。どこで検証するかとかによっていろんな方法が知られています。これもちょっと時間の関係で全体を割愛しますけど、これもサーベイ論文にあるので読んでもらいたいと思います。 227 | 228 | 229 | それから全然別のやり方でRefinementと呼ばれるようなやり方もあります。これ概念図だけピックアップしましたけど、右側の黒いのが対象の値でこれを言語モデルが生成したものだとしたときに、これ自身を入れてやって、もっかい生成させるというようなことだったり、あるいはこの生成したものに対してフィードバックを、左側の白いボックスに相当しますけど与えてやって再度生成するみたいな、そういったやり方をする研究もあります。これがリファインメントというようなやり方になってます。このリファイベントの代表的な研究がSelf-Refineと言われるような研究がありまして左が概念図でさっきとほぼ同じですけど、なんか最初タスクを言語モデルに与えましたと、こいつが出力した結果をSelf-Refineという研究だと自分自身で評価して、あとフィードバックを返すと。そのフィードバックを行った結果を使ってもっかいRefineすると、このRefineもこのSelf-Refineでは自分自身でやるんすけど、そういった枠組みの研究があります。右側例えばの生成例で上側のABCが一つの系列になってますけど、ダイアログが与えられたと会話が与えられたときに、このフィードバックって書いてあるのが、この左側の①のプロセスに相当するもの。これも言語モデルが出してるものでこのフィードバックを踏まえてリファインしたのがこの右側のものになります。ちょっと中身は読まないですけどこういった形で最初に生成させ、それ何を変えるべきかっていうのも言語モデル生成して、最後にその何を変えるべきかというのを踏まえてもう一度生成すると。これを何回も何回も繰り返すっていうのはやり方になってます。こんなんで性能上がるのかって思うと思うんですけど、これが何か上がるらしくてですね、最大50%ぐらい上がるような例もあるっていうふうに言われています。結果は結果なので、一旦これぐらいにします。 230 | 231 | 232 | これでほぼちょうどですけど、最後に少しあの、前半では全体の訓練時のスケーリングをする話を基本的にしましたけど、最近ではこの推論時の計算量っていうのも注目するような研究が増えてきています。代表的なGPT-o1とかですごく注目されてるかなと思いますし、今までやった方法、学んだ方法も結構出てきたと思いますけど、Promptingを工夫するとか、Decodingを工夫するとかいうので、それにも発展的な方法がいろいろ出てきていますし、Meta Generationっていうような枠組みで、DecodingだけじゃなくてそのDecodeした結果を最後どう使うかみたいな含めて、Meta Generationというふうに呼んでますけど、Paralell SearchとかStep Level SearchとかRefinementと言われるような枠組みの研究も出てきていますというような話をしました。 233 | 234 | 235 | 最後に補足して僕のパート終わろうと思いますけど、同じ計算資源のときにパラメータ増やすのよりも推論資源を増やすのが有効なのかっていうのが問いとしてあると思いますけど、o1の場合だと、訓練時のスケールは同じままって推論時のスケールを増やしたら、より賢くなりましたって話でしたけど、どっちにするのがいいのかっていう意味で言うと、GoogleDeepMindが8月に論文としてまして、Scaling LLM Test-Time Comupte Optimally can be more Effective than Scaling More Paremetersっていうことで、良いらしいというふうに言われてます。厳密に言うとこれなんかタスクによって違うということなので、良いとまで言っていいのかちょっと若干誇大広告な気が個人的にはしてますけど、そういったことを検証するような研究も出てきていますので興味ある人は見てもらえばと思います。 -------------------------------------------------------------------------------- /day3/data/LLM2024_day4_raw.txt: -------------------------------------------------------------------------------- 1 | 早速内容ですけど、目的はタイトルの通りですけど言語モデルスケール3について学ぶってことで、大規模言語モデルっていうふうに呼ばれてますけど、Cちょっとスケール足の話とか初回も少ししましたけど、これだけ大きくなっている一つの理由になってますのでそのスケール則ってどういうものなのかとかそれがなぜ重要なのかっていうところ、説明できるようなってもらうというところと、スケール則ってどうやって求めるんでしたっけというところを説明実装できるようないうところについて中心的に話していければと思ってます。 2 | あのスケールするっていうところではタイトルの通りなんですけど、ちょっとこれスケーリングPretrainingってなってるんですけれども、ちょっと最近はですね、このPretrainingだけではなくて、推論をスケールさせるというような話も出てきてましてせっかくなのでその最近の話題ということですレンジのスケーリングことでちょっとタイトル詐欺が入ってるんですけどPretrainingだけじゃない、スケーリングを扱うということでちょっと若干あのタイトル詐欺なんですけども、あの最近の話題ということで水土日のスケジュールについても話していきたいなと思っています。 3 | 練習では2のポイントに近いですけどスケール則を実際に求めるというところでその行動を実装できるなってもらうということを目的としています。では早速ですけど中身に入っていきたいと思いますちなみ余談なんですけどこれ実はDay4が去年から結構変わってまして、大変Day4だったものをDay4とDay48に分けてるんですけども、開けたはずのDay4がなぜか90枚スライドがあるという状況でしてちょっと若干早口になって申し訳ないんすけど少し資料を補足としてやっていただきながら自覚できるようにしてあると思いますんで、何かわかんないとこあったら資料読んでもらえると思います。 4 | 目標の通りですけれども大きく四つの話をしたいと思ってます。最初がスケール則とは何かそもそもどういうものだったかっていうところをご説明したいと思ってます。何かっていうのを先に学んだ後に、なんでこれ学ばなきゃいけないのかこれ知ってると何のいいことがあるのっていう意味でスケール則どういうふうに使ってるのか、他にどういうふうに使われてるのかってとこについて、2個目でお話したいと思います。 5 | 三つ目がそれをどうやってまとめるとか、練習に関係するところですね。最後に新しいトレンドっていうことでちょっと時間の関係で若干駆け足なるかもしれないけど解け推論時のフィーリングっていう最近の法案とかも出てますけどその辺で使われている技術について、SEP使われているとされている技術についてちょっとお話したいなと思ってまずスケール則とは何かっていうところですね。 6 | あの最初にも言った通りですけれど、スケールっていうのは、その大規模言語モデルを支える一つの大きな要素になってます。言語モデルっていうところがDay3でやりましたけれども、最近はTrasnformerになっていてまたNASAの中にもいろいろ種類があるということで発展的課題がまたコミュニティの方であるってありましたけども、基本的にはこの言語モデルを作る技術について言語ってどういうものですかっていうのをDay3でパターンだかなと思います。 7 | Day4ではこの大規模っていう側にフォーカスを当てまして、どうスケールさせるのかとか、なんでスケールさせることが有効なのかそれを支えるあのスケール則っていうものについてお話したいと思います。講義全体の位置づけも少しおさらいしておきますとます今日までが行った事前学習と呼ばれるような枠組みの話になります。 8 | また非常に大規模に学習データで訓練するっていうプロセスです。そっから次回以降はFine-Tuningってことで少しまた違う話をしますので一旦今日までがあの話の区切りだと思ってもらえればいいかなと思います。 9 | あの中身に入っていますこちらのこのスライド初回にもお見せした通りですけれども、第9言語モデルと呼ばれるものが進展していますということですあのこのサーベイ論文紹介にも紹介した通りですけど2023年の間に13回ぐらい更新されるぐらいモデルがどんどん出てると、2024年だって今年になってもいろいろなモデルが出たり、なんか巷で試されて実はこれは良くないんじゃないかと言われたりいろいろしているというような状況になっています。 10 | それからこれも最初紹介に示した通りですけども、基本的にこの裏側にはスケール化というのが進んできています。元々2018年ぐらいが117Billonだったのが、2020年には175Billonと1000倍ぐらいです企画なっていてGPT-4は1Billon以上だというふうに言われています。 11 | 今年でこのスケールっていうのがやっぱ重要な要素になってきています。こういうスケールしてるわけですけど、何でこんなにスケールしてるのかと、あるいはこのスケールするってどういうことなのかっていうのを今日は話していきたいと思います。 12 | 特にですね背景にあるのが一番よく出てくるのがスケール則と呼ばれる経験則でして、最初の方ではこの二つの論文を中心に説明したいと思います。一つがOpenAIが2020年に出した論文でして、スケーリングを4ニューラルarg1モデルというようなもん論文になっています。 13 | これちなみにGPT3が2020人、このスケーリング動画でたあの半径約半年後ぐらいに出てまして、GPT法の中でもこのスケーリング論の話とか出てくるようなものになってまして基本的にこのGPT3が開発された背景に、こういうスケール則の発見というのがあったというふうに言われています。 14 | もう一つがこれを少し拡張した形の研究として取りコンピュートPRRAGモデルというような研究がありますこれ、当時のDeepMind今GoogleDeepMindですけど、が出した論文でしてこのスケール則の考え方を使って、どういうふうなデータと、どういうふうなモデルサイズを用意すれば良いモデルが作れるのかっていうのを検証した論文になってます。 15 | この辺り中心にいろいろな研究をされながら今日はご説明できればと思ってます。早速スケール則とは何かっていうところについてこのスライド自体は初回に多分お見せしたかなと思いますけれども、どういうものかっていうのをおさらいすると、基本的な計算資源とデータサイズ厳密にtoken数ですね、学習に使っているtokenの数とモデルのパラメータ3LMっていうのに誤差、その学習に使ってるGopherですね。 16 | 学習とかせっかくテストデータにおける誤差の範囲他にこういう経験則がある経験則が存在するというような物差しことを指したあの言葉になってますどういう経験則かというと、例えばわかりやすいのが右側のパラメータなのでパラメータの方に注目すると、この青い点を打つっていうのが、実際にどれぐらいのパラメータを使ったときに、どれぐらいの損失だったかっていうのを表してますけれどもこれをプロットすると、両対数グラフ要は中のN乗みたいな形で書いたときですね、にこういった綺麗な直線になるというのが、要は補足になってますね。 17 | これがパラメータだけじゃなくて、データサイズだったり、計算量に対しても、横軸をそれぞれ変えてやったときに、同じように、こういった対数空間上での線形のスケールが成立しますよというような経験則になってます。 18 | 何か細かいことを一応書いてますけど一応これ要は前が実測値になってまして、この実線みたいなのが、予測値センタリングですね、が予測値に該当してます。これあとは他の2変数は十分に大きいと仮定したときのあの実験結果になってます。 19 | 一つ一つ見ていきますと今見たのと同じ順の話ですけどパラメータ数についてのピックアップしたのがこの次にあります。さっきと同じ50-50-70の9乗って形で横軸が対数の対数グラフになってまして例えば黒線とBで対数化された値になっています。 20 | これを見ていくと非常に綺麗にちょっとガタガタ若干してますけど、巨視的に見ると、大体この黒いフィッティングした線と同じオフィスPNGした線がこういう上のような形ですね。ちょっとこれが何の形かあとで説明します。 21 | それから次の図がデータセットのところについてピックアップしたものですけれど、さっき言った通りデータサイズって言ってるのは基本的なtokenの数だと思ってください。10の8乗tokenとか10の9乗tokenっていう単位これについてもパラメータと同様に点の値をプロットしていって、その間をフィッティングしてると非常に綺麗な関係が見えることがわかるかと思います。 22 | 最後が計算資源でして計算資源だけちょっとわかりにくいんですけどまず横軸がこのPFLOPSDというのになってます。PFLOPSテールっていうのが何かっていうとこのFLOPSというのが必要なLIMAPFLOPSをまず説明しますと一応補足というか知ってる人は聞き流してもらえばと思いますけど、KKさんごめんなさい計算量子計算ってのはどれぐらいの浮動小数点演算を行っているかを表してる単位だと思ってください。 23 | フードロス天山っていうのはコンピュータしてる人とあると思いますけどパラメータの足し算と掛け算に該当してまして細かい話するとディープラーニングの場合だと普通FP32とか案1、1Byteですね、の計算が使われるので1Byte同士のか、足し算みたいのが、1演算で掛け算は1演算という形で表せるになってます。 24 | これなんか右下に巨大何か一つMLPって書いてますけど、このWをかけるみたいな、これが掛け算に相当しますし、例えばバイアスを足すみたいなもバイアスを足すということで基本的にニューラルdtypeの計算ってのはこの浮動小数点のパラメータを足したりかけたりするというので払わすことからできることがわかると思います。 25 | 紛らわしいかっていうのちょっといっぱいしてもらえばいいと思うんすけど、この合計の浮動小数点演算の数を表すものとして、FLOPSというのが使われています。ごめんなさいこれまたこれもPが取ってるとおかしいですけどFLOPですね。 26 | 知ったあのフロックの数ということでFLOPSが使われる。さっき横軸がこの要は浮動小数点演算の合計の数になっているというふうに理解いただければと思います。ちなみに紛らわしいって書いたのが大文字のSのFLOPSっていうのもありましてこれがフローティングポイントOperationperSecondsっていうものになってまして、1秒間当たりどれぐらい計算できるかっていうのものになってます。 27 | これあのGPTの仕様書とか見ると、こういう感じでFP64とかFP32でQ例えばこれ189.5TFLOPSみたいな書いてあるんすけどこれは要は、1秒間当たり何回計算できるから、基本的にはハードウェアのうち、あの計算能力を表す単位だと思ってもらえばと思いますちょっと戻りますけど、ちっちゃい小文字のSは全体でどれだけ不動勝データが必要かというものを表すものといえば若干違うということでご理解いただければ要はあのスケール則を見たときに別に演算能力が上がってるわけではなくて合計必要な計算量が上がってるというふうに理解いただければいいとあのスケール則の方に戻りますと、パラメーターの話とパラメータってデータサイズトップスの話と若干違うとこがありまして県じゃなくてこういうなんか曲線が打たれてます。 28 | あのこの曲線っていうのが、あのこの図では、あのこともあるValueサイズって、学習したときの学習曲線をイメージして意味しています要は横にいくほどだんだん計算量がかかってくるので、学習したときのこの1個1個の学習曲線がこの青いなんか薄い線に対応してると思ってください。 29 | これ例えばこの特定のパラメータで学習したときの癖がこの1個の中に該当するということで、あの別のものが違う線でさらに違うものが、これ例えばN-gramN-gramRAGが一番大きいので計算時間計算量が書かれてN-gramが真ん中ぐらいでこれぐらいの計算でメニューがこれぐらいで計算するっていうような意味合いで声を変えてやったときにこのオレンジの線っていうのはこの一番ベストな条件この計算量を取ったときに、一番いい条件で落ちたときの3値っていうのがこのオレンジの点線でピッキングされてるというふうに理解いただければといて、これモデルサイズが小さいときは最初早く学習が進むわけですね計算量が少なくても性能が出るので計算量が少ない前提だと、モデルサイズがちっちゃい方がいい。 30 | 逆にモデルサイズが大きいと、なかなか学習が進まないんですけど、最終的な性能が良くなるということでこれが最適なパレットみたいのが存在するということです。ちなみにこれは読み方というか使い方に近いですけれど横軸テストロスをある値にしたときに、どういうものが良いモデルかっていうのは、この横をコピーといけば大体わかってこれちょっとパラメータ数がいくつかってのはわからないですけどこれN-gramぐらいがいいってことでめちゃめちゃ大きければいいというわけでも小さければいいわけではない。 31 | ことがわかりますし、自分が計算資源どれぐらい持ってるとき、何かパソコンを何か100台ぐらい持ってますとか何か自分が使える計算量がわかってるときにも、これをPと弾いてると大体これぐらいのパラメータを取ればいいんだなってことでそれがわかるということになります。 32 | これか。これがあのスケール則の詳細ですねコンピュートのところ以外は割とわかりやすいですけれど、こういった関係が整理するってのがスケール則と呼ばれているものです。若干細かい補足なんですけれどというかあの、さっきからこの謎の値が書いてあったと思うんすけど一応詳細に見ておくと補足なんで理解した人だけでいいですけれどこれよく見るとこういう劇場の関係になっていることがあります。 33 | 超えるっていうのがロスですね、IT化したりとか小さくしたいもので、ちょっとこれ若干順番が違うXCっていうのがこの例えば2.3掛け10の8乗みたいな、これがコンピュータの場合ですけど、計数に該当していて、アルファがこのマイナス0.05みたいな対応するということでこれ今コンピュータの図だけ出してますけど、他のものも同じような形をしています。 34 | これ何で両対数にすると線形になるのかっていうことですけどこれログ取れば明らかで、どっちもログ取るとこういう感じになるので、logX氏がこれ横軸なんで円形の普通の一時の資金でαが傾きのませんみたいな感じになることがありますαRAGXCが設定みたいなもんですねそういった形になるので、このスケール則というのはどういうこの劇場の形で書けるのでスケール速度というふうに言われています。 35 | ここまでスケール則の話をしてきましたけど、あのそうですねモデルサイズがまだ小さい状況でマスク不足っていうのを検証してましたけど、さっき半年後ぐらいにGPT3が出ましたよって言いましたけど、GPT3でもこのスケール則ってのを検証しているってことが報告されてます。 36 | さっきのあの学習のときと同じような図があの論文には載っていて、これ色がそれぞれパラメータの違いに該当しています黄色が一番大きくて青が一番ちっちゃい紫かな。ちっちゃいモデルになってます先行研究、さっきの今まで話しててね、より2桁オーダーが大きいモデルにおいても、これ厳密に言うとまだ収束してないように見えるんですけど、おおむねスケール則が成立してるっていうことがGPT制度の部分だと報告されています。 37 | あのスケール則についてここまで他の多分一番有名なのが先ほどのから話しているトレーニングじゃないスケーリング方ニューラルスケールの1モデルというものなんですけれど実はこのスケーリングっていうの自体スケーリング則スケール則が成立するってこと自体は、もうちょっと前から知られていたというふうに言われています。 38 | ちょっと僕ももしかしたらもっと昔からあるかもしれないんでそこが知ってる限り少なくとも1017年の論文では検証されているということが言われてますこのディープラーニングスケーリングREPLUGandREPLUGとなる勾配Qが出してるのと言います。 39 | ちなみにこれ今日話さないんですけど理論的にどういう条件だとスケール則が成立するのかっていうのを議論していたりもするのでもし興味ある人はこの論文化後スタンフォードのレクチャーでこの辺触れられていたのでもし興味ある人は見てるんだと思います。 40 | 少しだけ内容に触れますと、この論文では当然2017年なんでこれTrasnformerがあの出たか出ないかぐらいの時期なのであのTrasnformerのスケール則っていうのはやってなくて、LSTMだったり、RLHFというか、リカレントハイウェイネットワークっていうちょっと何ていうか、LMのアッシュみたいなものをですね、かなり時系列のデジタルネットワークみたいなことを使った研究になってます。 41 | これ見てもらうと横軸がトレーニングデータセットのサイズです縦軸がロスですね、ログスケールのロスになってまして、さっき見たのと同じような横線の図が広がっているというふうに描かれていることがわかるかと思います。 42 | そうですねこれそうですね厳密に言語モデルじゃなくて新トランスレーションの結果だったということです。あとは異なるとして対象モデルが違うってことでTrasnformerじゃないモデルを使ってますよとか規模も全然違うものですけれども、初期的にはこういった結果も知られていました。 43 | それをスケールアップさせたのが先ほどのプレーンの研究だというふうに説明できるかなと思います。それから野本のプレーのログに戻りますと、今言ったようなLSTMの比較みたいなLMにおける3スケール則みたいなことも、この論文でも検証されていまして、左側がモデル構造が違うんですね。 44 | Trasnformerの場合はスケール則がごめんなパラメータ数が横軸になってますけどこういうふうになると、LLMの場合の一掃にソヨンそうなのでそれぞれ計測と書くとこんなふうになりますよということで、Trasnformer以外のスケール則っていうのもあの研修をされて、深さについても検証してまして、これも他のモデルが何だったかちょっと忘れちゃったけどリスキーだったような気がしますけどそう変えたときにどういうふうな変化するかっていうのをこういった形でプロットするようなGENIACすることができます。 45 | ポイントはTrasnformer以外でも別にあのスケールするっていうのは成立概念だということです。なんでこんなTrasnformerだけ注目されてるのかってのは後で話します。それからこれは補足に近いですけど、Trasnformerの中でよく知ってる人だと最近はLlamaMIXWebXBERTって呼ばれるモデルがよく使われているということを知ってる人も多いと思いますけどこのミクスチャーExpertにおいてもスキル不足が存在するってことは言われています。 46 | 多分他にもあると思うけど代表的な論文で上の田上が初期でしたが最近なんかちょっとやり方を変えている足を検証してるものが下にありますけど、例えばこんな感じで実践が、先々がごめんなさい点線がうんTrasnformerで実践がこのミクスチャーGLUEExpertと呼ばれるモデルをスケールさせたときにどういう変化をするかというものを見ているものですけど、こういった形でできちゃうエキスパートにおいても、スケール則が成立するとかなり綺麗な関係が成立するということが言われています。 47 | このMoE自体はちょっと今日の今回ではないのでMoEがどういうモデルかっていうのはD8を楽しみにしておいてもらえばと思います。ここでポイントはTrasnformer普通単純なTrasnformer以外でも別にこのスケール則というのは、あの整理しているということです。 48 | それから基本的に今までの話は言語modelingなのでNEX次のtokenを予測するときの損失について縦軸としていましたけれど、それ以外にも他のドメインでもこのスケール則っていうのが整理するよっていうことも報告されています。 49 | これもオープンAIが出してた論文で、これ左がイメージのモデリングでテキストとイメージとかビデオとか3計算とかイメージっていうテキストとか何かHRAG今までと一緒ですこういったものが他のドメインでも成立するよということも言われています。 50 | それからもう一つの論文でChinChillaトレイにコンピュータOptimalっていうものがありますけれど、ここまでが1変数を制御している横軸1変数にして、他の二つの変数については際限がないというか無限にあるという前提での経験則でしたけど2変数を制限した場合の経験則っていうのも知られています。 51 | それが有名なのがこのトレーニングコンピュータOptimalRAGじゃないじゃなく1モデルというものでしてそれが左側の実験結果右側がPaLMと呼ばれるGoogleが出したValueのバージョン2のPaLM2というモデルがありますけど、そのときにホワイトペーパーから取ってきたやつになってます。 52 | どっちも同じようなふうになってますけれどこれていうのはそれぞれの、例えば左側を見ると、各68とか119と書いてますけど、この618とかが計算量使える計算量を意味してると思ってください。なので例えば左上のこの薄いやつ、薄い緑のやつは、それぞれ6位、18の計算量を使って、何かを変化させるというものです。 53 | その何かっていうのがこのパラメータになってまして、例えばこのなんかどこだろうこの線を見ると、いくつなんだろうわかんない100ミリオンとか、この線を見ると300ミリオンとかぐらいをパラメータに割り当てた場合、ということになってます。 54 | 当然パラメータを増やしたり減らしたりするだけだと、計算量っていうのは変わってしまうので、この場合は学習時間は固定していてtoken数を変更させているっていうなものになってます。なんでこの丸はそれぞれ学習時間とtoken数が変化しているというふうに思ってください。 55 | これをいろんな計算時間に対して見ていくと、どれも概ねこのYouTubeというかの下側に凸な形になっていることがわかると思います。これ要は最適な値がありそうだということですね。ある計算量を考えたときに、めちゃめちゃ巨大なモデルを使うでもなく、めちゃめちゃ小さいモデルを使うのではなく、最適な値がありそうだということがわかると思います。 56 | 右側も同じような結果ですね、あの形は微妙に違いますけど大体同じようなことわかると思います。これがあのChinChillaの論文でしてこれの使い方もまた後で話します。ここまでがあのスケール速度は何かのまとめでして、スケール則っていうのは毎回おさらいすると、計算資源とデータセットからMetaと誤差の間にはこういった経験則がそうですよ。 57 | こういうべき乗則で書けますよっていうのが原則でした。両対数グラフで線形なるなこれを他の業態数取ってやるとわかるということも説明しました。それから一番有名なのはTrasnformerで本当のスケール則ですけど、それ以外のモデルでも成立しますし、言語以外のタスクでもスケール則ってのは確認されていますという話をしました。 58 | それから1変数を制御するのではなくて複数の変数を制御するような経験則も知られていて有名なBERTChinChillaと呼ばれる経験則がありますChinChilla論文って確かにここまでで計測スケール則の話をしたんですけどChinChillaのところで少しパラメータ数とデータセットサイズ、それぞれいじって計算量を肯定しますよって話をしたのでちょっとその補足をしておきたいと思います。 59 | これよく出てくる式、あの経験則のケアの近似式なんですけど、学習に必要な計算量ってどうやって計算してるんですかっていう話があると思います。これはどういうかけるパラメータ数掛けるtoken数っていうふうに、あの計算されることが多いです。 60 | これ例えばGPTの場合だと175BillonがNに相当します。0.3テラとBillon寺川TB寺tokenがDに相当するそれに6を掛けたもので3.1草刈ってますFLOPS23兆FLOPSですねファン.14掛ける10の2030分になるというふうに計算できます。 61 | これ近似って書いてある通りこの6っていうのは近似なんですけどこれは概ねなんでこの6かっていうと、これ興味ある人だけでいいですけど、いっそ当たり1パラメータあたりのMLP層における計算が6回あるということに起因しています。 62 | これアニメーションついてるんすけどアニメーションめっちゃ早いんですけど、基本的には1回MLPを計算するときに、フォワード方向でかけて立つので2回でパッカードではそれが乗り換え行われるバックヤードで入ってきたときにかけて出すのと、自分が外に出す値を計算するときにかけて出すっていうので、4回あるので、大体1パラメーター当たり6回計算するというのでこの6という数字が使われています。 63 | これなんかよく謎の式として出てくるので少し補足しておきます。ちなみにこの近似って書いてある通りこれはあの雑な近似ではありますというか下にMLP層におけるって書いてある通りなんですけどMLP以外の層も当然あるのでそれ以外の層では違う値が実際には厳密には必要になります。 64 | ただこれ系列長が短い場合だと、ちょっと僕も正確にはEOSMLPの計算量の方が転移より低い系列が長いと転移の計算で大きくなってしまうんですけどそうじゃない場合基本的にMLPの計算力のがあの膨大なので大体無視できるでしょってことでこういった式が使われているそうです。 65 | 最近はどんどんtoken数伸びてきているので、若干無視できなくなっている結構あり、あるかなと思いますしあと正式な正確な式の例っていうのもありましてこの実装とかだとこういう何かちょっと例えばエンベディングにかけるこれは同じですね。 66 | アテンションにかける3かけるシーケンシャルレングス×2モデルは×KeySize×Llamaヘッドみたいな、こういった感じで厳密にこういうふうに計算することができます。ただスケール則をやるときにはそのモデル同士の比較ができればいいので、多分そんなに気にしないで適当にこのFLOPSっていうのを計算してるんだと思います。 67 | はい。ちょっと補足でした。ちなみに補足がてらよく使うので見にくい図というか、考えてみてもらえばということでちょっと答え書いてないんですけど、さっきFLOPS大文字のSの概念と、あの計算量の話GPT3だと例えばこの3.14掛ける10の23乗ありますよって話をしましたけど、これを使うと大体GPT3の学習にどれぐらい計算時間が必要か、百選これが1000基あるといったときにどれぐらい必要かっていうのを見積もることができます。 68 | このFLOPSとかよくこういう計算にも使うので、興味ある人はちょっと計算してみてもらえばと思います。はい。今やらなくて大丈夫、回ればいいだけです。ちなみに言語モデル開発しようと思うと、めちゃめちゃよく使う意識でして、WebLab.Billonとか開発したときは裏でこういうCとかめちゃめちゃ飛び交ってました。 69 | これは裏話でここまでスケールとは何かについて話しましたがこれは何かっていうのは一旦理解できたとそういうべき乗の関係にあって両対戦すると線形になるんですね。1変数じゃなくて2編成の経験則ってのもあるんですけど、そういったことは理解できたと思いますけれどそれどうやって使うんですかっていう話を次していきたいと思います。 70 | これ同じですねこの劇場の関係が成立しますよというようなツールです。これがスケールアップでした。これどうやって使うんですかっていう説明をするにあたってちょっと使われてる例をいくつかピックアップしました。これGPT-4のテクニカルレポートから取ってきた図ですけれどこの緑が実際のGPT方のパラメータ数だというふうに言われていますこれが1になってます。 71 | これに対してGPT法では、それより先輩の1ぐらいちっちゃいモデルでのスケール則を図って、推論した結果、これぐらいの性能になるだろうっていうのをプロットして作ったっていうふうに書かれています。要はこれGPT-4例えばいくつかわかんないっていう現実はわかんないすけど、このパラメータを訓練する前にそれより小さいモデルでスケール則を検証して、これぐらいいくんですねと、これしっかりちゃんと性能上がるんですね。 72 | ちょっと確認したというふうに言われてます。こういった形で自分が持てる思ってるモデルを作ろうと思ったときに、モデルを大きくすることに意味があるのかっていうのを見積もることができる、あるわけですよね。これが使い方の一つですね。 73 | ちなみにこれ公開されてないときにちょっと計算してたんですけど、今回リーク情報出る前にちょっと計算してるんすけどこれ1で何か左端に100100ペタコンピュートっていうのがある。100下手だったかな、ピコピコですね。 74 | パピココンピュータっていうのが書いてあって、多分普通に計算すると、10の10乗より大きく500ってか、機構が確か10の10乗で、これが1だとしたときにもGPT政権はGPTになるので、500ピコが例えば10の3乗ぐらいのTrasnformerだとしたら少なくとも1取れるようにならなきゃいけないってことで、何かこれで大体推論できるなと思ってたにしました。 75 | しました。これはただの余談ってその他の使い方としてモデルを、どちらがモデルが良いかっていうのを比較することもできます。これどちらのモデルが良いのかって比較しなければ別に同じパラメータで比較すればいいんじゃないのって思うかもしれないんですけど、もうちょっと厳密に言うと、モデルをスケールさせるとしたらどっちのモデルがいいですかっていうように予測することができるように左側のこの二つの図は最初の前半でも少し話しましたけれど、左側がモデル構造を比較して、右側からMetaを比較してるものですけど、左側だと例えばTrasnformerだの方が、どうやらこれスケールさせてってもずっとよさそうだということがわかります。 76 | LSTMをスケールさせていってもTrasnformerを逆転することは、どうやらなさそうであるということが何となくわかるかなというふうに思います。右側の例だとそう変えてますけど例えば50-50ぐらいしかパラメータがないときに比較すると、これどんどんモデルが良いかわからない、どの層がいいかよくわかんないと思うんすけど、このスケール則を測ってると、どうやらパラメータ数を増やしていける前提だったら6層以上にそう増やしていった方が、そうするとということが予想が立つわけです。 77 | このポイントは実際にこれを計算する前にフィッティングしてやればあの外装というかちょっとさっきまでわかるということですね。GPT法の場合と一緒ですけどこれGPT実際作る前にこの曲線を引くことができるので、そういう形で小さいパラメータをすくっていろんなパラメータを検証してあって、大きいパラメータでどれぐらい、どういう関係になるかってのを予測することができるっていうのが一つの重要なポイントです。 78 | こうしたやり方はあの研究でもよく使われてまして、これもあのまんまという研究自体はちょっと紹介しないですけど、あの子のママと呼ばれる論文から引っ張ってきた図になってます。これ横軸って河川がいろいろ書いてるんすけどそれぞれ何か違うモデルですね。 79 | 後から来るとか、Trasnformerとの様ですネットとかH3++ってこれがなんかTrasnformerじゃないやつらって試してる作られてるものなんですけど何が言いたいかというと論文でも実際このスケールをさせたときに、どういうふうになりそうかとスケールさせたときに、この論文兼主砲天和は勝てるのかっていうのが、実際研究されてたりもします。 80 | ちなみにこの論文ちょっと若干余談なんですけどTrasnformerとTrasnformer+Plusっていうのがありまして、これ確かTrasnformer+バッチのLlamaでLlamaで使われる構造で、Trasnformerが元のGPTの講座なんですけど、これ見ると結構スケール則が違うということもわかると思います薄いオレンジとオレンジでなので結構構造が大事だよということも実はこの部でわかったりします。 81 | あとは馬場っていうのがこの紫ってなんか強そうなんですけど、これが何かっていうのは多分D8でやるんじゃないかなと思ってこれも楽しみにしてもらえばとそれからちょっと似てる話ですけど効率性を効率的にやろうと思ったときにどうすればいいのかっていうのを話しることもできます。 82 | 左側が横軸がtokenす縦軸がパラメータ数になりますけど、マクロ浅子田地区じゃないごめんなさい色がパラメータにどうしてもこれ見ると、例えばこれ若干直感に反することを言ってるところあるんですけど、あるパラメータを固定したときには、基本的にパラメータ数が大きい5面あるtoken数固定したときには、大きいパラメータのモデルが、サンプル効率がいいロスが下がりやすいということがこの結果からわかったりします。 83 | それから逆に右側が横軸がコンピュートになっていて、色がモデルサイズであることは変わらないんですけどこれを見ると例えば10のマイナス3乗の生産量があるときには、これぐらいのコンピュータを使えばいいということがわかったりします。 84 | これ別に大きければいいというわけね。この辺の理屈は小さなモデルだと、学習がロスが下がらなくなるというのでちょっとあのスケール則を書いてるときに、なんか言っこのプロット説明者と同じような話ですけど、あの計算量が与えられたときに、どうやら、別にパラメータを増やせばいいわけではないいうことはわかりこの計算量が与えられたときにどれぐらいのバジェット、どれぐらいのパラメータ数とtoken数に割り振ればいいのかっていうのを、あの計算しようとしたのが、先ほど出したこの2変数の関係っていうふうに言ったChinChillaと呼ばれる経験則になってます。 85 | 現実のChinChillaモデルの名前なんですけどなんかChinChilla則って言われたり、ChinChillaケースって言われたりするので、何かその辺を丸ごと足してChinChillaというふうに、大体呼ばれてると思えばと思います。 86 | 左側の図は先ほど見せたのと同じで、それぞれの点があの色が、あの計算量使ってる計算量に相当してましてパラメータ数を変更させた場合です。右側の図が増えてるんですけど、これを各FLOPSで最適なパラメータに直したものっていうのが、この中央でこれを同じようにデータ数token数に対して、直したものが中央になります。 87 | っていうのはこれパラメータ見ると何か例えば13のE21を使えるんだったら、あのこの一番下のやつをピックアップしたやつか、この右側の真ん中の方に行ってきていて、同じように1枚、Q2で場合はぴって引っ張ってくるみたいな、やったときにどういう関係があるかっていうので、これを見てみると何となく大体線形っぽい関係にわかります。 88 | 右側がtokenの場合の同様の例ですね。これをフィッティングしてると、例えばこれ貴重な値ですけど10度24錠よりちょっと低いぐらいの計算量が使えますよっていうときには、パラメータ数は63Billonにすればいいと同じように同じところが、これ取られてるんすけど、データ数がどれぐらいすればいいかっていうと、1.4という4tokenにすればいいということがわかります。 89 | エラtokenですねごめんなさいこうして作られたのがChinChillaと呼ばれるモデルになっています。これあのGopherっていうモデルがありまして、Gopherがこのじゃないごめんなさい、DeepMindが出してた、この前に出してたモデルで、これが280Billonでtoken数が0.3テラtokenというふうに言われてます言われますかというふうになってます。 90 | 要はこいつと比べるとこのChinChillaっていうのはモデルサイズがちっちゃいんだけど、あのトレーニングtokenを増やしたとえそれはどうやって決まったかというと先ほど言った通りですけど経験則に基づいてどのぐらいのバジェットをパラメータに割り振ってどれぐらいのバジェットを遠くに割り振るかっていうのを、この経験則によって決めちゃった値を使ってやってやるということをしているものです。 91 | 結果としてはこれで多くのケースより巨大なモデルに勝てるということが実験上示されています。これ左側のやつが実験で結果じゃないんで、ちょっと実験結果飲みたい人はこの元の6ミリValueだと思いますけど、あの巨大なモデルにかかってるということでこの関係性が良さそうだということが示されています。 92 | ちなみにこれも余談ですけどこのの求め方このChinChilla則ってのは実は何かいくつかの方法で求められてまして、それぞれ大体同じような経験則が出るってことが知られています。この関係式っていうのがよくこれも知られてまして、大体最適なtoken数っていうのが、パラメータ数に意地をかけたもの。 93 | これと同じですね。70Billonを訓練するのに1.4とBillonのtokenを使うということになってますのでこの式の20っていうのも謎のマジックナンバーとしてよく出てくるので覚えておくといいと思います。 94 | 先ほどPaLM野津PaLM通の相撲を見せましたけど。普通でも同じような経験するが成立しますよということが言われています。これは同じなので割愛します。ここまで見るともうこのtoken数だけであのパラメータ数を、あるパラメータ数に対してtoken数を決めればいいあるいはtoken数が固定データセットサイズが限られてんだったら、それよりそれに対してあるパラメータ数で決めてしまえばいいんじゃないかっていうふうに思うかもしれないけど、これはそんなに簡単ではないということも調べています。 95 | って何のことですかというか今最適な割り振りっていうふうに言ってたじゃないかというふうに思うかもしれないすけど一つの観点は、さっき話したのは、訓練のバジェットだけを考えていたという点が、現実的ではあまりない。 96 | いうことがあります。これ左がのが訓練時のFLOPSをどうするかっていう話で、8日6時までに作ったやつ13Billonのモデルが例えばこんな感じのスキルをしたともうちょっとちっちゃいモデルを大量のデータで学習させたモデルは、あのこの黒い黒というか、若干項目色ですかね、みたいな線だとする。 97 | 私です。これロスが同じような値を取るモデルっていうのをピックアップすることができて、この状況で比べるとやっぱり訓練のFLOPSは13.がいいとChinChillaOptimalな作り方をする方がいいということが左の図からはわかるんですけど今度はこの推論するということを考えたのがこの真ん中の図にあります。 98 | これ当然なんですけど、実際にはこのモデル学習した後に、皆さんGPTとかChatGPTとか使ってると思いますけど、みんなが使うときにも計算量がかかるわけです。この推論のときに使うかかるコストっていうのは当然ちっちゃいモデルの方が小さいさっき推論のコストが2回分とか22×Bだっていうふうになんかちらっと言いましたけど、それからするとこのコストが推論実にかかり続けるので、学習のコストだけじゃなくて色も考えると、あの名Billonの方が常にちっちゃいわけです。 99 | これが例えばこのぐらいのtokenだったときは、このぐらいのサイズときにこれ要はゆくゆくは絶対推論コストが低い方が逆転するんですよね。この右側出したやつですけどそうすると、このスピードん時のコストまで考えてやっていいモデルを作った方がいいんじゃないかっていうのが、あの考えてる人もいたりします。 100 | これがあのBillonとChinChillaLIMAというな研究になってる。この論文の結果を一つだけピックアップしましたけど、右上に書いてある数式は6NDPRっていうのが、学習時の盗掘に対して6Lさっきのマジックナンバーですね。 101 | おかげでDFってのがインフラ筋のものでこれにかけてる2をかけてなバックワードがないので、4回分の計算はないからですね。左の図がなんか綺麗な図が書いてありますけど、フランスtokenを増やしていったときに、ChinChillaケースに対して何倍にしていくかっていうのを、この色が表してます。 102 | これも当たり前なんですけど推論回数が多くなるほど、ライフタイム全体では、あの学習token増やすと要はケースを大きい方にする方が、あの、あるロスを見たときに、あの一番よくないということがわかります。あるtokenすですね、いたときに一番良いっていうのは変わっていくよっていうことがわかると思います。 103 | こういう考え方もあるねっていう話をしましたけど、これはLlamaとかでも、実際token数を増やすことが多いということにも多分繋がってるっていうふうに言われています。ちょっとさっきの図を持っていましたGopherというのが、DeepMindが元々作ってた巨大なパラメータを少なめのデータセットで学習したものになってまして、ChinChillaっていうのがさっき言った経験則によって学習したものになってます。 104 | これがあの係数が20このtokenをパラメータであった値が、20、20倍のtokenを利用しましょうということでしたけど、例えばLlama2だと学び7Billonでも70Billonと同じtokenを使ってますけど、7Billonのものに関しては1.8とBillonのtoken使ってるので、285倍のケース全然違う値を使ってるDense70Billonは28個Llama3に関しても、21470Billonが214倍、400倍Billonの方が37倍ということで、このChinChillaオプティカル実際に巨大なモデル、巨大なtokenの学習を使っているケースもよくあります。 105 | ここまでのまとめが透ける方どうかスペースをどう活用するかって話をしてきました。使い方としては投資するかどうか、大きいモデルを開発するかっていうのを割と予測できるというような使い方もありますしこれが実際実際GPT法の開発にも繋がったというふうに言われています。 106 | モデル選択もできるとパラメータを増やしたときに、どっちが読まれるかっていうのを実際にパラメータを試すことなく、そのモデルの巨大なモデルを作ることなく、その研修ときってのがいいとこだとそれからどれぐらい計算資源を割り当てるかっていうのでChinChillaを決めるっていうものがたり、そのChinChillaOptimal以外にも推論コストを考えたときの最適なtokenを議論する。 107 | が研究もなされています。ここまでがスケール速度活用法でしたけど少しだけ補足をいくつかしておきたいと思います。この予測不可の予測可能だというふうに言いました。スケール則ってのは先ほど言ったように、あの劇場の関係で綺麗なフィッティングができるので、予測ある。 108 | ある意味予測可能なわけです。だけど同時にこのサプライズというか予測不可能な部分っていうのもあるっていうふうにも言われていますこれがこれAnthropicっていうオペから独立したというか枝わかれした会社がありますけど、その会社が出してる部分ってPrettyandSurpriseにRAG1MetaRAG1モデルGPTAlignモデル化ブログに触れられていますけどこの予測不可能だっていうのがEmergentビート板、例えばEmergentabilityと呼ばれるなあの現象になっています。 109 | これ初回で話した通りですけど、例えばパラメータを増やしていったときに、元の計算が突然できるとかそういったものがありますというのが知られています。これ初回でフェアのために何か本当にそうなのかみたいな話を少し言いましたけどこれは実はミラージュ厳格なんじゃないかっていうなの研究も出ています。 110 | 何を言ってるかっていうといろいろな話をしてるんですけど、これ要は性能の測り方に答えよるでしょうということを言ってなんか横軸が大変だってうんそもそも何かそのEmergentって言ってるけど何が創発なんすかみたいなそういった議論もありますけど、少なくとも何かこういうなんてQに何かできることができるようになるように見えるということが、起こるという期待もこういった言語モデル開発を加速させる要因の一つなのかなというふうに思います。 111 | それからこれもあんまり本題と関係ないですけど面白い現象としてブロッキングと呼ばれるものを存在しています。これ言語モデル自体ではないんですけど、ロッキングっていうのはめっちゃか学習させたモデルをずっと学習させ続けると、突然テスト生の場があるというような現象になってます。 112 | これはなんかARBって書いてますけどこういうに演算の関係を表しててだったらとかこういう何か表があってこの穴埋め問題を解くというようなタスクになってます。物とか、例えばそういうものをデータ数倍とか、この赤線がこの訓練の性能で、緑があのテスト制度なんですけど、横軸これオプティマイズのステップになっていて、なんか中の2乗とかから10の5乗とか中のスケール、じゅ、中のN乗のスケールなんですけどなんか最初めちゃめちゃ隠しほぼ解けないのに学習させた状態での学習を続けると性能が上がるというふうに報告されています。 113 | これもある意味予測不能なわけですね計算量を増やし続けると、大きなことが起こるという現象として知られています。これ何が起こってるのかみたいな研究としてはホットトピックの一つとして知られていて、これなんかREPLUGエディションの学習だっていうプレゼンテーションが学習されると、何かが起こるんだったら学生続けるとこういう表現なるんだという研究もありますし、何がどういう良い表現を獲得してるっていうことに繋がってるのかみたいなことも研究されていたりもします。 114 | 個人的には面白い時かなと思ったんすけど、今日の話は少し関係ないねこのぐらいにしておきます。それから予測不可能な改善という話とも関係するんすけど、実際にはロスを下げたいというよりは何かのダウンストリームの性能を上げたいことが多いと思います。 115 | そういう意味で断3の性能を見た研究もありまして、これは学習以外のデータに対してのテストロスがどうなるかっていうのを見ています。だから仮タスクというよりはデータ分布が変わったときですね。これ見ると大体オフセットが違う実際のロスは違うんですけど、傾きはどうやらスケールしてとかっていうことがわかると、それからこれもコンピュートを指定したときですね横軸にとったときにパラメータはどのぐらいのパラメータだと一番いいかっていうので、これも大体綺麗な関係があるかわかると思います。 116 | 一方でこういう軽い助けてあげるのかっていうとEmergentアビリティの話とだいぶ関係がだいぶ違うこと言ってると思うんですけど、実際にはこのいろんな関係があるっていうことも、検証されています。これ多分GPT法の論文でも議論されているものですしこのGPT3の論文でも言われてるものですけど、綺麗に上がるようなタスクもあれば、急に上がるものもあれば全然上がらないものもあるということで、実際には必ず上がるわけじゃないっていうのは注意してもらえばと思います。 117 | それからこういう何かインバース経路っていうのも知られていて急に悪くなるような、あのタスクも存在するっていうふうに言われていて、去年とかだと何かインバーススケーリングプライズっていうことで、スケールさせるほど精度が悪くなるタスクを見つける賞金が出るみたいな、そういうコンテストも開いてたりします。 118 | はいここまでがスペースの使い方ってことで話してきました。ちょっとどうしようかな1回質問に行ってもいいですよちょっとスケール足の話を全体でしてから質疑を受けようかなと思ったんで具体的な求め方についてもずっと話します。 119 | さっきからチラチラ言ってた通りなんすけど基本的にこれどう図るかっていうと、基本的にはいくつかの条件で実験してフィッティングするって言ってんのは、すごい単純に言ってしまうとそうなります。左側GPTC4の論文年から取ってきた図として説明したもんですけど、グレーのやつを例えば実験してみて、これぐらいのロスになるんだなっていうので、フィッティングするとこういうアプリになります。 120 | ちなみにこれ、なんでこれ鉛直せん断ないんだっていうのをすぐ説明しなかったですこれ縦軸が実は普通のロスと違ってBits-per-wordっていうのをやってて、多分2条スケールの押すんなってるからだと思います。 121 | 右側も同じですね。この手各店について何かいろんな設定で実験してやって、それを結果を見るということをしてますけどよくよく考えるとスケールさせるときにモデルサイズどうすればいいんでしたっけとか、何をどういじるとモデルサイズが大きくなるんでしたっけ、どういうふうに言えばいいんでしたっけとかですね。 122 | あのモデルサイズ変えたときにハイパーパラメータってどうすんでしたっけそういった細かい問題が出てくる。最初の方ですけどモデルサイズどう変化させるかっていうので、前回やった、こういう図があると思いますけどモデルサイズ変えようと思ったら別にパラメータあの層の数を増やしても、いいわけですし、この埋め込みの次元各tokenの次元を増やしてもいいわけですし、その各随所に出てくるこのフィードフォワードネットワークっていうのの中間層の人上げてもいいですしヘッドを増やしてもそういうのあのパラメータ自体は上がるということで、これどれをどのぐらいやるんですかっていうのが細かく考えると重要になってきます。 123 | この辺は元の論文でも一応議論されてまして、これ三つを出してるんすけど例えば真ん中のがアスペクト比っていう、モデルのエンベディングのサイズですね。Tモデルっていうものを頭数で割ったもの、アスペクト比という縦横比みたいなもので幅と深さの比率をアスペクト比っていうふうにこの論文では呼んでいますけどこういったものを変えて実験してみたっていうのが最初の最初じゃないオペのスケーリングLoRA本部ではなさい基本的にはこの辺見るとなんかあんまり性能に影響ないっていうふうにこの論文では言ってますけど、この辺を気にしながらモデルスケールすることが多いです。 124 | 気にしながらっていうのの実例を出した方がわかりやすいと思うので、実際にこれ開発者じゃないので、あの結果を見て推論してるだけなんで嘘ついてるかもしれないですけど例えばLlama3の論文を持ってくると8Billon70Billon405Billonって層の数モデルDimension埋め込みの数次元ですね、フィードフォワードの次元、アテンションの数っていうのを、こういうふうにしたよっていうふうに言われてます。 125 | これさっき言ったアスペクト比、縦横比がこのモデルイメージをLayerで割ったものなんで、これそれぞれ見ると128102.430ってことでこれ大体100から130ぐらい、なんかおおむね同じような値になっていることがわかると思います。 126 | それからモデルとフィードカードの事件数ですね、モデルの時限数に対しフィードバード事件数は3.5倍になっているということがわかります。これ約3.5かな。ちょっと自信ないですちょっとちゃんと計算したとかいった計算したら、ちょっと違ってたら教えてほしいんすけど大体3.5倍ぐらいあったとアテンションのヘッドはこのFAの時限数と同様にスケールしたモデルの次元と同様にスケールしているということがわかる。 127 | こういった感じで幅とかを大体同じようなケースで、なるべく伸ばしてくと、ただこれ、指定したパラメータ数にしようと思ったときに、当然どっかは完全には固定できないので、若干変わりますけど大体同じような比率でスケールさせているというようなことがわかると思います。 128 | それからこれセレブらすGPTっていう別の論文ですけどこれの論文を見るとこれちょっとさっきの時Llamaよりだいぶちっちゃいですけど、アスペクト比が大体上から76.877.785.385.3みたいな感じで大体これも同じような値になってて、モデルとFN次元がそれぞれ決まっていると、4.0となってて、ヘッドはちょっとこれもLaMDAとだいぶ規則変化してますけど、大体この辺を揃えているということが経験上わかるかなともう一つの話がハイパーをどう変化させるかで、これも同じ3プラスGPTというのを見てみると、例えばこのLRDKのタイプ理系ですね各種時に理系をどうするかで、ちっちゃいモデルとリニアにしてんだけど、1.3と2点弾はなぜかコサインで6.7リニアで小さいBillonはコサインみたいな謎のことをしていることがわかると思う学習率を見てみると、6.0E-野木6.090-4乗だったのが一点に影っていうのマイナス4乗になってたりこの辺が変化してるということがわかると思います。 129 | これなんでかっていうと、学習したことは方だったら得られたのと、言語モデルに限らずだと思いますけど、あのモデルのサイズとかによって結構この辺の最適なパラメータ数ってのは変わるっていうのは何となくわかるかなと思ってます。 130 | 左が何かある論文から持ってきた幅を変化させたときの学習率がどのぐらいだといいのか、最適なのかってのをプロットしたものになってます。色が幅に対応していて、横軸が学習率を6にしたものですね。これ見ると例えば8192の場合だと、このマイナス16ぐらいにいいとこいますし、128の場合だとマイナス10ぐらいということです。 131 | 要は経験則としてモデルサイズを大きくしたとき、大きくごめんなさい。大きくなってるのが大きい大きくしたときには学生とちっちゃくするってのが大体だということがわかると思います。SalesGPTもそうなってますバッチサイズを大きくするといいってのも経験則としては言われているこの図はちょっと関係ないですけどそういう形で実際にはそのパラメータをスケールさせたりする必要があるということに注意してください。 132 | ちなみにこれある論文って言ったんすけどμTrasnformerGopherっていう論文でして、この論文は実はこのパラメータを変える必要があるってことを主張してるわけじゃなくて、何かいい感じの方法を使うといい感じってだいぶ大雑把に言いましたけど、この幅によらず最適なこの学習率の値バッチサイドもそうなんすけど、同じできるよってことを主張してる研究だったりします。 133 | ちょっと面白いので説明したいとこなんすけど興味ある人は、論文見てもらえばと思います何してるかっていうと簡単に言うと、れるじゃないや、あの重みの書記官ときとかに、入力の数とかに応じて初期化を変えると思うんですけど、それにいたことを学習率とか、努力のウエートにして掛けてやるとするとこれがウエイトに対して最適な学習率っていうのがこのケースだけであの最適になるということを示している研究だったりします。 134 | ちょっと興味ある人は読んでもらえばと思い、これm3の何か活用方法だけここに行ってますけど、これUPって書いてあるのが、このさっき言った怪しい怪しくはないんですけどただいい、いい感じの初期化をするということをしたものでしてそれやると学習室が変わらなくても、いいよってことがSalesGPTの場合だと言われています。 135 | ちなみにLlamaの場合はなんかちょっと論文見たんですけどちょっと厳密によくわかんなくてなんか参照したよっていうふうに書いてあるんすけど実際ラーニング例とは何かちょっといじってるみたいなので、この辺どうやってるかっていうの多分論文とか図形なモデルによると思うのでちょっとモデルを実際興味ある人は見てもらえばと思います。 136 | スケール則の話がここまでで終わりなのがトレーニングですけどそこの話終わりますけど、基本フィッティングすればいいんですけどモデルサイズを気にする方モデル再度スケールさせるときにはハイパーどうするかっていうところが、あの結構実験的にやられてまして、ボディサイズについては大体なんか幅アスペクト例集みたいなもの、ぱぱっと深さの比率みたいなものを維持しながら受けさせますし、学習率とかはスケジュール、明日への徐々に小さくする、大きくしたときに徐々に着するとか、ある意味Trasnformerという技術を使ってたりするということをご理解いただければと思います。 137 | 講義に戻ります。ちょっと練習の時間もあるのであと20分ぐらいで駆け足になりますけど、最後最近のスケールトレンドって話で生のGENIACLMの話をして終わろうと思いですねちょっとモチベーションから話すと、ちょっと頭で考えてみてほしいとか見れば一瞬で思うとんですけどバナナの色は何ですかって言われたときと、今日の講義聞いた上で、ゲームソフトの問題は何だと思いますかって聞かれたとき、多分あの考えることが違うと思うんですね。羽の色なんですかっていうと一瞬黄色ですねもしかしたら緑かもしれないけどぐらいですかね物によるかなみたいなおもちゃだったら違うかもみたいな、だんだんあの、考えていくといろいろ出てくるかもしれないすけど、少なくともスケール足の問題なんだと思いますかって聞かれたときに、今日の話からするとスケール則っていうのはこういうものだからどうだろうこの辺が問題かなみたいな考えとやっぱ思考としては違うってことは何となく思うかなと思います。なんか人間的にはこの二つって全然違うしあの、答えるのに必要な考え方っていうのも違うように思えるわけです。スケールって言ってる7Gのスケールって言ってるのはこういった形で、あの簡単なものについては簡単に答えてもいいですし、そうじゃなくて、あの考えなきゃいけない問題に対しては、考える時間を、に計算式を使うというふうにしたときに、これいいことがあるのかっていうような話になってます。二つで、ちょっと順番が前後しますけどこれの仕組みは言語モデルでも効果的ですかっていう話と、これをどう実現できるかっていう、こういう二つの話が最近のトレンドとして出てきています。効果的ですかっていうのが、最近大湾と呼ばれる論文が論文じゃないか、モデルがオペルから出ましたプレビューとして出てますけどこの法案で注目されていますこれあの論文にROMってかブログにあるとイエスって右側が訓練時の計算資源をスケールさせたときに、初めて何かロジックのベンチマークがあるんですけどこれをがどうなったかで何となくスケールしてると右側がテストTimeコンピュートっていうふうに書いてると思うんすけど、水温時に計算資源を増やしたときあるモデルを使うんだけど、簡単に答える方法と深く考えて答える方法みたいでだんだんコース計算式を増やしていったときに、性能がどう変わるかっていうのでこれもスケールしていってるということがわかると思います。こういった形で、要は考える時間をどうやら推論時に使うと計算資源を推論使うのはいいことがありそうだということがわかります。 138 | そうすると次の話はどうやって計算資源をスケールするのか計算量スケールするのかって話ですけど実は一番簡単な方法はいくつかもうこの講義でも触れていて一つは例えばチェーン相当使うっていうのもその一つです。チャームソフトを使うと出力するプロtoken数が増えますよね。その思考の過程を自分で出力するので、これってのはある意味計算資源を増やして使っちゃってるとフェアに言うと、直接答えるのと、tokenをいっぱい出して使う。答えるのは違うわけですよね。ありていに言うとかかるお金が違うわけですからそれから目にちょっとICLっていうのも、あのDay2で多分少し話したのかなと思いますけどPretrainingときに学習テキストに入れるQを、サイズを増やしていくとどうなるかっていうのでこれもスケールしますよということが言われてましたけどこういった形で簡単にこのPromptingのときに出力するtokenだったり、入力するtokenっていうのをいじってやると、計算量を増やすことができるので、なんでこの辺の研究から見ても明らかなんですけど、本当に間うまく考えるのに計算式が使えれば性能が上がりますよってのは、既にあの子のあのSTまでにも話してた話だと思います。 139 | それからDay3でもDecodingっていう仕組みを、あの話したと思います。このDecodingにもいろんな方法があってグリーンDecodingだと単純に一番いいやつを選んでいく、一番確率が高いやつ選んでいくので、すごい単純ですけど、こういうトップKeyを取るとかトップ系を取るとかして、最後に一番いいやつを選ぶみたいなことをすると、これも結局計算をたくさんしてることになるわけですね。1個選ぶわけじゃないので、次に評価しなきゃいけないものがどんどん増えていくわけなので、こういった形で増やすっていうのも一つのやり方として存在しています。これ何かDecoding方法の一覧ですけどこれ興味ある人いたら言ってください。 140 | どうしようかな。時間も関係あるんでなんか最近の例を一つだけ紹介するとコントラスト美Decodingというのは例えばいまして、Xとリップスティックって書いてあるこの軸にそうとする方法の一つ外部のモデル別のモデルを使う方法なんですけどこの研究によると単純に自分が自分自身のこの出力の確率を使うよりも、なんかしょぼい言語モデルの確率を割ってやったものを使った方が、現地ちょっとログで言うと引いたものですね。方が良い指標になっているということが知られています。これなんか多分僕、なんかよくよく話しすぎちゃうのってよく出てくるものじゃないものについてエキスパートモデルが高く値を出していたLlama5161ってのは普通はあまり出さないわけですけどモデル出さないんだったら、このベースラインとして引いてあるっていうか終わってると良いよっていうようなDecoding方法も出てきています。ここで今つらくSteamDecoding詳細を理解してほしいということよりは、Decodingのときにその他のモデルを使って性能を上げると、要はこのアマチュアモデルの計算がさらに増えるわけですけど、こういった形の概念として新しい手法も出てきていたりしますということで、 141 | それからここまでの実行DINGの話は基本的に次のtokenをどう選ぶかという話をしてましたけど、Decoding、次のtokenをどう決めるかだけじゃなくて、全体プロセス全体としてどういうふうな生成を行うかっていうのを一段広く、維持した上から見ましょうというのでMetaGenerationという言い方をするような研究も出てきています。このfromDecodingMetaGenerationというのが、6月かな、に出たサーベイでして、理論人の言語モデルで使うアルゴリズムを広くサーベイしたものになってます。この衣装がMetaGenerationて会員になってますけどちょっとこれがどういうものかについて説明していきたいと思います。そうね三つぐらい種類があるっていうふうに書かれてますられるサービスSEPVersaceリファインメントですけどちょっとこれ何か説明より具体を見た方がわかりやすいと思うんで、それぞれ具体を説明していきたいと思います。最後にこれをずっと振り返ったこういうもんだなと思ってもらえばいいと思います。 142 | パレスサーチの方法の一番代表的なのがベストベールと呼ばれるものです。これ要は、これをtokenだと今までの話と同じなんすけど文を何個か生成してみて一番いいやつを選びましょうっていうのが、このパラレルサーチの一番簡単なベストWebっていう方法ですね。一番いいのっていうのがいろいろ考えられて、LLMのスコアを使うっていうのが、これがビール産地とかそういう普通の美味さとかに相当するものですけどベリファイと呼ばれるような学習した評価機を使うような研究だったり、GLUEとか機械翻訳のときにGLUEとか、特定の指標を使うみたいで何かの外部の指標を使って、とりあえずN構成して、一番いいやつを、後で選びましょうとそういうアプローチになってます。ちょっとMBRDecodingの話しようかと思うんすけど時間がだいぶ生BERTかもしれない簡単に言うとこれがMRDecodingというのが最近機械翻訳で注目されてる一つの方法らしいんですけど、この例の中で言うとGLUEとかの指標を使っていいものを選ぶというような研究の例になってます。そうですね伊藤ですけどこの何か期待値の中にいうて書いてあるけど、このUっていうのが、このさっきこのスライドでスコアと、相当してましてGLUEとかMetaとか、いろんなものを使っています。これは実際にこれ人間のサンプルがこういう交換するとこういうコンセプト必要なんですけど、これをこのMBRだとやめていって牝馬の代わりにモデルから生成したものをサンプリングして期待値を取るみたいなことをやるのがMBRDecodingというやれるやり方になってるんすけどちょっと詳細が知りたい人は、だいぶわかりやすい資料が上がってましたのでこのURLから飛んでもらえばと思います。ここではこの、要はスコアを選んでいいものを選ぶというようなやり方でいろいろ発展してきてるんだということを理解していただければいいと思います。 143 | それからBestBillonとはちょっと違う方法として、N個を生成した後に、それらを集約するという意味では、Day2Day2Falconシステムをこの枠組みの一つとして入り、あの説明されます。セル仕込ん試験紙は下のようなもんですけど推論のことを応用したものになってて、言語モデルにCoTでのいろんなニーズにパスを出させて、まじないずB'zパスって書いてあるけどそのリズムパス出した後にこの一番よく出てきた答えっていうのを最終的にお答えするというようなやり方になってると思います。これもN個の中から1個選んでるわけじゃないんですけどN個出力して、それを集約する形でこのNCoTはそれぞれ独立に動くのでパラレルに動くので、さらに動かして最後集約するという意味でこのペアパラレルサーチの枠組みの一つの代表的な手法になってます。このアグリゲーションどういうふうにこの結果を集約するか、すいません。だったり、どういうふうにスコアをつけるかっていうので、いろんな手法が知られてましてこれもさっきのサーベイ論文に入っているので興味を引いていけばてもらえればと思います。 144 | 今のパラレルサーチの中でマジョリティーコーティングとベストWebっていうのが出てきましたけど、これあるタスクこれ確か米数学のタスクだと思うんすけどタスクが載ってないんで興味ある人はこの論文見てもらえばと思うんすけど、例えば比較すると、普通にマジョリティーコーティングするのがこの黒線で、あの青線がこのベストオブLMで終わるって書いてあるけど、これアウトカムSupervisedリワードモデルってもので要は全体に対して、これが正しいか正しくないかを推定するのリワードモデル結果を水の推計するヤードモデルってのを用意してあって、Value&モデルにとって一番良かったやつは最後選ぶっていうそういうやり方になってます。マジョリティーボディーはN個出して最大のものを選ぶっていうやり方です。これやると比べると、リワードモデル使った方がいいよということが反応だと言われています。一応補足なんすよGPTが別にそのリワードモデルとか作る必要ないので簡単な方法であるっていう利点はあって、ベスト弁の方のRMっていうのは、リワードモデル別で学習しなきゃいけないってのが欠点としてあることは一応補足しておきます。 145 | それからこのオレンジをスルーして話したんですけど実際この論文はこのオレンジのやり方を提案してる論文になってます。それがこのPRMっていうものでして、PRLってのはプロセスSupervisedリワードモデルっていうふうに呼ばれています。あるいは何かあの論文にあったプロセスリワードモデルって普通に読んでるものもあります。これは全体に対して合ってる間違ってるっていうのを予測するんじゃなくて、このプロセスごとに合ってる間違ってるみたいのを予測するモデルを作る。というものですこれあの数学の問題みたいなプロセスがわかりやすいですけど、最初にレッツコールTheair夢DXみたいな感じでそれぞれのバスが正しいか正しくないかみたいなこれはユーザーが提出してる例ですけど、ユーザーの提出した例を使ってプロセスの正しさを予測してるということをしています。これをやるとさらに性能が上がるっていうことが左の図からわかりまして、これちゃんと説明しなかったですけど横軸がN個なんで生成するものですね数になってますけど、これ増やしていくとさらに性能が上がるということがわかります。このPRみたいなプロセスに対して評価を付けるっていうな説明をしましたけど、こういったやり方をするのがステップレベルサーチっていうふうにさっきのサーベイLaMDAとまとめられています。ごめんなさい。この一つ前の研究ではこのプロセスLayerとモデルのキーワードを使って、あのテスト勉強を選ぶってやり方をしましたけど、ベスト以外にも、このプロセスごとに塊を作って生成していってビームサーチみたいなことをすることもできます。要は最初この2個が良さそうだからこのニコイチに怒らせて、この、こいつからまた発生させて2個作ってまた次のステップ3の選んでみたいな。これを繰り返すっていう方法です。これ丸が一つのプロセスtokenじゃなくてプロセスに対応してましてというのが普通のビルサーチの違いですけどそういった研究もあります。ちなみにそういった研究もありますって言いましたけどこの月Web外っていうのをDay2で多分やったと思うんすけどこれがほぼ似たようなことをしていますみたいなことっていうのはこのリワードモデルっていうの使う代わりに言語モデルで、この状態が戻るいいものかこっからこれゲームを24-0ですけど、達成24になる数字作り方をしてくださいっていうときに、あのもう絶対に達成できない場合は、あの言語モデルがフィールドするというようなことをしているものですけど、そういった形でやってるっていうのも、言語モデルをあのリワードの評価として使っているあの例だとステップレベルサーチのあの例だと、いただければと思います。これもさっきと同様ですけど探索方法とか検証するステップの違いですね。どこですっていう検証するかとかによっていろんな方法が知られています。FSでは先読みしてやるみたいなことです。これもちょっと時間の関係で全体を割愛しますけど、これもさBloomにあるのでやってみようかなと 146 | それから全然別のやり方でリファインメントと呼ばれるようなやり方もあります。これ概念図だけピックアップしましたけど、右側の黒いのが、なんかあの、対象の値ってこれを言語モデルが生成したものだとしたときに、これ自身を入れてやって、もっかい生成させるというようなことだったり、あるいはこの生成したものに対してフィードバックを、右側の左側の広い広いボックスに相当しますけど与えてやって再度生成するみたいな、そういったやり方をする研究もありますこれがリファインメントというようなやり方になってます。このリファイベントの代表的な研究がセルフリファインと言われるような研究がありまして左が概念図でさっきとほぼ同じですけど、なんか最初タスクを言語モデルに与えましたとこいつが出力した結果をあのモデル自分自身のこのセリフは研究だと自分自身で評価して、あとフィードバックを返すとそのフィードバックを行った結果を使ってもっかいリファインすると、このリファもこのセリフに入ると自分自身でやるんすけど、そういった枠組みの研究があります。右側例えば政令で上側のABCが一つの系列になってますけど、ダイアログが与えられたと会話が与えられたときに、このフィードバックって書いてあるのが、この左側の①のプロセスに相当するこれも言語モデルが出してるものでこのフィードバックを踏まえてリファインしたのがこの右側のものになります。ちょっと中身は呼ばないですけどこういった形で最初に生成させそれ何を変えるべきかっていうのも言語モデル生成して、最後にその何を変えるべきかというのを踏まえても改正するとこれを何回も何回も繰り返すっていうのはやり方になってます。こんなんで性能上がるのかって思うと思うんですけどこれが何か上がるらしくてですね、最大50パーぐらい上がるような例もあるっていうふうに言われています。結果は結果なので、一旦これぐらいしか 147 | これでほぼちょうどですけど、最後に少しあの、前半では全体の訓練時のスケーリングをする話を基本的にしましたけど、最近ではこの推論値の計算量っていうのも注目するような研究が増えてきています。代表的なGPT法案とかですごく注目されてるかなと思いますし、今までやった方法までを学んだ方法も結構出てきたと思いますけど、Promptingを工夫するとか、Decodingを工夫するとかいうので、それにも発展的な方法がいろいろ出てきていますし、まめちMetaGenerationっていうような枠組みで、Decodingだけじゃなくてそのレコードした結果を最後どう使うかみたいな含めて、GENERATIONSMetaGenerationというふうに呼んでますけど、ただヴェルサーチとかステップでVersaceとかリファインメントと言われるような枠組みの研究も出てきていますというような話をしました。 148 | 最後に二つ補足しておくのパートだろうと思いますけど同じ計算資源のときにパラメータ増やすのよりの推論資源を増やすのが有効なのかっていうのが問いとしてあると思いますけどオープンあの場合だと、訓練時のスケールは同じままっていうのちょっとスケールを増やしたら、より賢くなりましたって話でしたけど、どっちにするのがいいのかっていう意味で言うと、GoogleDeepMindか。1月に論文としてまして、スケーリングLLMthisTimeコンピュート口真理ちゃん日は増えてるっていうことで、良いらしいというふうに言われてます。厳密に言うとこれなんかタスクによって違うということなので、良いとまで言っていいのかちょっと若干誇大広告な気が個人的にはしてますけどそういったことを検証するような研究も出てきていますので興味ある人は見てもらえばと思います。 -------------------------------------------------------------------------------- /day3/data/llm04_eng.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "content": "それからこれCerebrasGPTっていう別の論文ですけどこれの論文を見るとこれちょっとさっきの時Llamaよりだいぶちっちゃいですけど、アスペクト比が大体上から76.8,77.7,85.3,85.3みたいな感じで大体これも同じような値になってて、モデルとFFNの次元がそれぞれ決まっていると、4.0となってて、ヘッドはちょっとこれも論文だとだいぶ規則変化してますけど、大体この辺を揃えているということが経験上わかるかなと。もう一つの話がハイパラをどう変化させるかで、これも同じCerebrasGPTというのを見てみると、例えばこのLR Decayのタイプですね、学習時にDecayをどうするかで、ちっちゃいモデルとlenearにしてんだけど、1.3と2.7はなぜかCosineで6.7はLinearで13BillonはCosineみたいな謎のことをしていることがわかると思う。学習率を見てみると、6.0E-04(6.0掛ける10の-4乗)だったのが1.2*10^-4になってたりこの辺が変化してるということがわかると思います。" 4 | }, 5 | { 6 | "content": "これなんでかっていうと、学習したことは方だったら得られたのと、言語モデルに限らずだと思いますけど、あのモデルのサイズとかによって結構この辺の最適なパラメータ数ってのは変わるっていうのは何となくわかるかなと思ってます。" 7 | }, 8 | { 9 | "content": "左が何かある論文から持ってきた幅を変化させたときの学習率がどのぐらいだといいのか、最適なのかってのをプロットしたものになってます。色が幅に対応していて、横軸が学習率をlogにしたものですね。これ見ると例えば8192の場合だと、このマイナス16ぐらいにいいとこいますし、128の場合だとマイナス10ぐらいということです。" 10 | }, 11 | { 12 | "content": "要は経験則としてモデルサイズを大きくしたとき、大きくしたときには学習率をちっちゃくするってのが大体だということがわかると思います。CerebrasGPTもそうなってます。バッチサイズを大きくするといいってのも経験則としては言われている。この図はちょっと関係ないですけど、そういう形で実際にはそのパラメータをスケールさせたりする必要があるということに注意してください。ちなみにこれある論文って言ったんすけどμTrasferっていう論文でして、この論文は実はこのパラメータを変える必要があるってことを主張してるわけじゃなくて、何かいい感じの方法を使うといい感じってだいぶ大雑把に言いましたけど、この幅によらず最適なこの学習率の値バッチサイズもそうなんすけど、同じできるよってことを主張してる研究だったりします。" 13 | }, 14 | { 15 | "content": "ちょっと面白いので説明したいとこなんすけど興味ある人は、論文見てもらえばと思います。何してるかっていうと簡単に言うと重みの書記官ときとかに、入力の数とかに応じて初期化を変えると思うんですけど、それに似たことを学習率とか、出力のweghtにして掛けてやるとするとこれがweightに対して最適な学習率っていうのがこのケースだけであの最適になるということを示している研究だったりします。" 16 | }, 17 | { 18 | "content": "ちょっと興味ある人は読んでもらえばと思い、これμTransferの何か活用方法だけここに行ってますけど、これμPって書いてあるのが、このさっき言った怪しい怪しくはないんですけどただいい、いい感じの初期化をするということをしたものでして、それやると学習率が変わらなくても、いいよってことがCerebrasGPTの場合だと言われています。" 19 | }, 20 | { 21 | "content": "ちなみにLlamaの場合はなんかちょっと論文見たんですけどちょっと厳密によくわかんなくてなんか参照したよっていうふうに書いてあるんすけど実際learning rateとは何かちょっといじってるみたいなので、この辺どうやってるかっていうの多分論文とかにあると思うのでちょっとモデルを実際興味ある人は見てもらえばと思います。" 22 | }, 23 | { 24 | "content": "スケール則の話がここまでで終わりなのがトレーニングですけどそこの話終わりますけど、基本フィッティングすればいいんですけどモデルサイズをスケールさせるときにはハイパラをどうするかっていうところが、あの結構実験的にやられてまして、モデルサイズについては大体なんか幅アスペクトratioみたいなもの、幅と深さの比率みたいなものを維持しながら受けさせますし、学習率とかはスケジュール、徐々に小さくする、大きくしたときに徐々に着するとか、例としてμTrasnferという技術を使ってたりするということをご理解いただければと思います。" 25 | }, 26 | { 27 | "content": "講義に戻ります。ちょっと演習の時間もあるのであと20分ぐらいで駆け足になりますけど、最後最近のスケールトレンドってことで「推論時のスケーリング」についての話をして終わろうと思います。モチベーションから話すと、ちょっと頭で考えてみてもらえれば一瞬でわかると思うとんですけど、「バナナの色は何ですかって言われたとき」と、今日の講義聞いた上で、「スケール則の問題は何だと思いますか」って聞かれたとき、多分あの考えることが違うと思うんですね。「バナナの色なんですか」っていうと黄色ですね。もしかしたら緑かもしれないけど、物によるかなみたいな、おもちゃだったら違うかもみたいな、だんだんあの、考えていくといろいろ出てくるかもしれないすけど、少なくとも「スケール則の問題なんだと思いますか」って聞かれたときに、今日の話からするとスケール則っていうのはこういうものだから「どうだろう」「この辺が問題かな」みたいな考えとはやっぱ思考としては違うってことは何となく思うかなと思います。なんか人間的にはこの二つって全然違うしあの、答えるのに必要な考え方っていうのも違うように思えるわけです。推論時のスケールって言ってるのはこういった形で、あの簡単なものについては簡単に答えてもいいですし、そうじゃなくて、深く考えなきゃいけない問題に対しては、考える時間に計算資源を使うというふうにしたときに、これいいことがあるのかっていうような話になってます。これの仕組みは言語モデルでも効果的ですかっていう話と、これをどう実現できるかっていう、こういう二つの話が最近のトレンドとして出てきています。効果的ですかっていうのが、最近o1と呼ばれるモデルがOpenAIから出ました。プレビューとして出てますけどこのo1で注目されています。これあのo1の論文ってかブログにある図で、左側が訓練時の計算資源をスケールさせたときに、AIMEというロジックのベンチマークがあるんですけど、accuracyがどうなったかというと、何となくスケールしてる。右側がtest-time computeっていうふうに書いてると思うんすけど、推論時に計算資源を増やしたときあるモデルを使うんだけど、簡単に答える方法と深く考えて答える方法みたいでだんだん計算資源を増やしていったときに、性能がどう変わるかっていうので、これもスケールしていってるということがわかると思います。こういった形で、要は考える時間をどうやら推論時に使うと、つまり計算資源を推論時に使うのはいいことがありそうだということがわかります。" 28 | }, 29 | { 30 | "content": "そうすると次の話はどうやって計算資源をスケールするのか計算量スケールするのかって話ですけど実は一番簡単な方法はいくつかもうこの講義でも触れていて一つは例えばChain of Thoughtを使うっていうのもその一つです。Chain of Thoughtを使うと出力するtoken数が増えますよね。その思考の過程を自分で出力するので、これってのはある意味計算資源を増やして使っちゃってると。フェアに言うと、直接答えるのと、tokenをいっぱい出して答えるのは違うわけですよね。ありていに言うとかかるお金が違うわけですからそれからMany Shot ICLっていうのも、あのDay2で多分少し話したのかなと思いますけどIn-Context LearningのときにIn-Contextとして入れる集合を、サイズを増やしていくとどうなるかっていうので、これもスケールしますよということが言われてましたけど、こういった形で簡単にこのPromptingのときに出力するtokenだったり、入力するtokenっていうのをいじってやると、計算量を増やすことができるので、なんでこの辺の研究から見ても明らかなんですけど、基本的にうまく考えるのに計算資源が使えれば性能が上がりますよってのは、既にDay3までにも話してた話だと思います。" 31 | }, 32 | { 33 | "content": "それからDay3でもDecodingっていう仕組みを、あの話したと思います。このDecodingにもいろんな方法があってGreedy Decodingだと単純に一番いいやつを選んでいく、一番確率が高いやつ選んでいくので、すごい単純ですけど、こういうトップPを取るとかトップKを取るとかして、最後に一番いいやつを選ぶみたいなことをすると、これも結局計算をたくさんしてることになるわけですね。1個選ぶわけじゃないので、次に評価しなきゃいけないものがどんどん増えていくわけなので、こういった形で増やすっていうのも一つのやり方として存在しています。これ何かDecoding方法の一覧ですけどこれ興味ある人いたら言ってください。" 34 | }, 35 | { 36 | "content": "どうしようかな。時間も関係あるんでなんか最近の例を一つだけ紹介するとContrastive Decodingというのは例えばいまして、Extrinsicって書いてあるこの表中の軸に相当する方法の一つ、外部のモデル別のモデルを使う方法なんですけどこの研究によると、単純に自分が自分自身のこの出力の確率を使うよりも、なんかしょぼい言語モデルの確率を割ってやったものを使った方が、厳密にはLogの場合には引いたものですね。この方が良い指標になっているということが知られています。これなんか多分僕、なんかよく話しすぎちゃうものやよく出てくるものじゃないものについてエキスパートモデルが高く値を出していた場合、例えば「1961」ってのは普通はあまり出さないわけですけど、しょぼいモデルも出さないんだったら、これをベースラインとして割ってやる(引いてやる)と良いよっていうようなDecoding方法も出てきています。ここでContrastive Decodingの詳細を理解してほしいということよりは、Decodingのときにその他のモデルを使って性能を上げると、要はこのアマチュア(しょぼい)モデルの計算がさらに増えるわけですけど、こういった形の概念として新しい手法も出てきていたりしますということです。" 37 | }, 38 | { 39 | "content": "それからここまでのDecodingの話は基本的に次のtokenをどう選ぶかという話をしてましたけど、Decoding、次のtokenをどう決めるかだけじゃなくて、全体(プロセス全体)としてどういうふうな生成を行うかっていうのを一段広く、上から見ましょうというのでMeta Generationという言い方をするような研究も出てきています。このFrom Decoding Meta Generationというのが、6月かな、に出たサーベイでして、推論時の言語モデルで使うアルゴリズムを広くサーベイしたものになってます。この2章がMeta Generationて概念になってますけどちょっとこれがどういうものかについて説明していきたいと思います。そうね三つぐらい種類があるっていうふうに書かれてます。Parallel Search, Step Level Search, Refinementですけど、ちょっとこれ何か説明より具体を見た方がわかりやすいと思うんで、それぞれ具体を説明していきたいと思います。最後にこれを図を振り返ってこういうもんだなと思ってもらえばいいと思います。" 40 | }, 41 | { 42 | "content": "Parallel Searchの方法の一番代表的なのがBest of Nと呼ばれるものです。これ要は、これをtokenだと今までの話と同じなんすけど文を何個か生成してみて一番いいやつを選びましょうっていうのが、このParallel Seacrhの一番簡単なBest of Nっていう方法ですね。一番いいのっていうのがいろいろ考えられて、LLMのスコアを使うっていうのが、これがBeam Searchとかそういう普通のBeam Searchとかに相当するものですけど、valifierと呼ばれるような学習した評価機を使うような研究だったり、GLUEとか機械翻訳のときにGLUEとか、特定の指標を使うみたいに、何かの外部の指標を使って、とりあえずN個生成して、一番いいやつを、後で選びましょうとそういうアプローチになってます。ちょっとMBR Decodingの話しようかと思うんすけど、時間がだいぶ迫ってるかもしれないので簡単に言うと、これがMBR Decodingというのが最近機械翻訳で注目されてる一つの方法らしいんですけど、この例の中で言うとGLUEとかの指標を使っていいものを選ぶというような研究の例になってます。そうですね今言った通りですけどこの何か期待値の中にUて書いてあるけど、このUっていうのが、このさっきこのスライドでのスコアに相当してましてGLUEとかMeteorとか、いろんなものを使っています。これは実際にこれ人間のサンプルを用いたこういう関数が必要なんですけど、これをこのMBRだとやめていてHumanの代わりにモデルから生成したものをサンプリングして期待値を取るみたいなことをやるのがMBR Decodingというやれるやり方になってるんすけど、ちょっと詳細が知りたい人は、だいぶわかりやすい資料が上がってましたのでこのURLから飛んでもらえばと思います。ここではこの、要はスコアを選んでいいものを選ぶというようなやり方でいろいろ発展してきてるんだということを理解していただければいいと思います。" 43 | }, 44 | { 45 | "content": "それからBest of Nとはちょっと違う方法として、N個を生成した後に、それらを集約するという意味では、Day2でやったSelf-Consistencyをこの枠組みの一つとして説明されます。Self-Consistencyは下のようなもんですけど推論のCoTを応用したものになってて、言語モデルにCoTでのいろんなReasoning Pathを出させて、'Marginalize out reasoning paths'って書いてあるけどそのreasoning pathを出した後にこの一番よく出てきた答えっていうのを最終的に答えとするというようなやり方になってると思います。これもN個の中から1個選んでるわけじゃないんですけどN個出力して、それらを集約する形でこのN個はそれぞれ独立に動く(パラレルに動く)ので、さらに動かして最後集約するという意味でこのParallel Searchの枠組みの一つの代表的な手法になってます。このアグリゲーションどういうふうにこの結果を集約するかだったり、どういうふうにスコアをつけるかっていうので、いろんな手法が知られてましてこれもさっきのサーベイ論文に入っているので興味を引いていけばてもらえればと思います。" 46 | }, 47 | { 48 | "content": "ここまでパラレルサーチの中でMajority VotingとBest of Nっていうのが出てきましたけど、これあるタスクこれ確か数学のタスクだと思うんすけどタスクが載ってないんで興味ある人はこの論文見てもらえばと思うんすけど、例えば比較すると、普通にMajority Votingするのがこの黒線で、あの青線がこのBest of N、ORMって書いてあるけど、これOutcome-supervised Reward Modelってもので、要は全体に対して、これが正しいか正しくないかを推定するリワードモデル、結果をすいてするReward Modelってのを用意してあって、Reward Modelにとって一番良かったやつを最後選ぶっていうそういうやり方になってます。Majority VotingはN個出して最大のものを選ぶっていうやり方です。これやると比べると、リワードモデル使った方がいいよと言われています。一応補足なんですがMajority Votingは別にそのリワードモデルとか作る必要ないので簡単な方法であるっていう利点はあって、best of Nの方のORMっていうのは、リワードモデルを別で学習しなきゃいけないってのが欠点としてあることは一応補足しておきます。" 49 | }, 50 | { 51 | "content": "それからこのオレンジの線をスルーして話したんですけど実際にはこの論文はこのオレンジのやり方を提案してる論文になってます。それがこのPRMっていうものでして、PRMってのはProcess-Supervised Reward Modelっていうふうに呼ばれています。あるいは論文によってはProcess Reward Modelって普通に読んでるものもあります。これは全体に対して合ってる間違ってるっていうのを予測するんじゃなくて、このプロセスごとに合ってる間違ってるみたいのを予測するモデルを作るというものです。これあの数学の問題みたいなプロセスがわかりやすいですけど、最初にLet's call the numerator xみたいな感じでそれぞれのパスが正しいか正しくないかみたいな、これはユーザーが提出してる例ですけど、ユーザーの提出した例を使ってプロセスの正しさを予測してるということをしています。これをやるとさらに性能が上がるっていうことが左の図からわかりまして、これちゃんと説明しなかったですけど横軸がN個なんで生成するものの数になってますけど、これ増やしていくとさらに性能が上がるということがわかります。このPRMみたいなプロセスに対して評価を付けるっていうな説明をしましたけど、こういったやり方をするのがStep Level Searchっていうふうにさっきのサーベイ論文にまとめられています。ごこの一つ前の研究ではこのProcess Reward ModelのRewardを使って、あのBest of Nを選ぶってやり方をしましたけど、Best of N以外にも、このプロセスごとに塊を作って生成していってビームサーチみたいなことをすることもできます。要は最初はこの2個が良さそうだからこの2個を生き残らせて、こいつからまた発生させて2個作ってまた次のステップ3の選んでみたいな。これを繰り返すっていう方法です。これ丸が一つのプロセス、tokenじゃなくてプロセスに対応してましてというのが普通のBeam Searchの違いですけどそういった研究もあります。ちなみにそういった研究もありますって言いましたけどこのTree of SearchっていうのをDay2で多分やったと思うんすけどこれがほぼ似たようなことをしています。みたいなことっていうのはこのリワードモデルっていうの使う代わりに言語モデルで、この状態がいいものか、こっからこれGame of 24の例ですけど、足して24になる数字の作り方をしてくださいっていうときに、あのもう絶対に達成できない場合は、あの言語モデルがFailとするというようなことをしているものですけど、そういった形でやってるっていうのも、言語モデルをあのリワードの評価として使っているステップレベルサーチの例だと思っていただければと思います。これもさっきと同様ですけど探索方法とか検証するステップの違いですね。どこで検証するかとかによっていろんな方法が知られています。これもちょっと時間の関係で全体を割愛しますけど、これもサーベイ論文にあるので読んでもらいたいと思います。" 52 | }, 53 | { 54 | "content": "それから全然別のやり方でRefinementと呼ばれるようなやり方もあります。これ概念図だけピックアップしましたけど、右側の黒いのが対象の値でこれを言語モデルが生成したものだとしたときに、これ自身を入れてやって、もっかい生成させるというようなことだったり、あるいはこの生成したものに対してフィードバックを、左側の白いボックスに相当しますけど与えてやって再度生成するみたいな、そういったやり方をする研究もあります。これがリファインメントというようなやり方になってます。このリファイベントの代表的な研究がSelf-Refineと言われるような研究がありまして左が概念図でさっきとほぼ同じですけど、なんか最初タスクを言語モデルに与えましたと、こいつが出力した結果をSelf-Refineという研究だと自分自身で評価して、あとフィードバックを返すと。そのフィードバックを行った結果を使ってもっかいRefineすると、このRefineもこのSelf-Refineでは自分自身でやるんすけど、そういった枠組みの研究があります。右側例えばの生成例で上側のABCが一つの系列になってますけど、ダイアログが与えられたと会話が与えられたときに、このフィードバックって書いてあるのが、この左側の①のプロセスに相当するもの。これも言語モデルが出してるものでこのフィードバックを踏まえてリファインしたのがこの右側のものになります。ちょっと中身は読まないですけどこういった形で最初に生成させ、それ何を変えるべきかっていうのも言語モデル生成して、最後にその何を変えるべきかというのを踏まえてもう一度生成すると。これを何回も何回も繰り返すっていうのはやり方になってます。こんなんで性能上がるのかって思うと思うんですけど、これが何か上がるらしくてですね、最大50%ぐらい上がるような例もあるっていうふうに言われています。結果は結果なので、一旦これぐらいにします。" 55 | }, 56 | { 57 | "content": "これでほぼちょうどですけど、最後に少しあの、前半では全体の訓練時のスケーリングをする話を基本的にしましたけど、最近ではこの推論時の計算量っていうのも注目するような研究が増えてきています。代表的なGPT-o1とかですごく注目されてるかなと思いますし、今までやった方法、学んだ方法も結構出てきたと思いますけど、Promptingを工夫するとか、Decodingを工夫するとかいうので、それにも発展的な方法がいろいろ出てきていますし、Meta Generationっていうような枠組みで、DecodingだけじゃなくてそのDecodeした結果を最後どう使うかみたいな含めて、Meta Generationというふうに呼んでますけど、Paralell SearchとかStep Level SearchとかRefinementと言われるような枠組みの研究も出てきていますというような話をしました。" 58 | }, 59 | { 60 | "content": "最後に補足して僕のパート終わろうと思いますけど、同じ計算資源のときにパラメータ増やすのよりも推論資源を増やすのが有効なのかっていうのが問いとしてあると思いますけど、o1の場合だと、訓練時のスケールは同じままで推論時のスケールを増やしたら、より賢くなりましたって話でしたけど、どっちにするのがいいのかっていう意味で言うと、GoogleDeepMindが8月に論文としてまして、Scaling LLM Test-Time Comupte Optimally can be more Effective than Scaling More Paremetersっていうことで、良いらしいというふうに言われてます。厳密に言うとこれなんかタスクによって違うということなので、良いとまで言っていいのかちょっと若干誇大広告な気が個人的にはしてますけど、そういったことを検証するような研究も出てきていますので興味ある人は見てもらえばと思います。" 61 | } 62 | ] -------------------------------------------------------------------------------- /day3/data/llm04_eng_nofix.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "content": "それからこれセレブらすGPTっていう別の論文ですけどこれの論文を見るとこれちょっとさっきの時Llamaよりだいぶちっちゃいですけど、アスペクト比が大体上から76.877.785.385.3みたいな感じで大体これも同じような値になってて、モデルとFN次元がそれぞれ決まっていると、4.0となってて、ヘッドはちょっとこれもLaMDAとだいぶ規則変化してますけど、大体この辺を揃えているということが経験上わかるかなともう一つの話がハイパーをどう変化させるかで、これも同じ3プラスGPTというのを見てみると、例えばこのLRDKのタイプ理系ですね各種時に理系をどうするかで、ちっちゃいモデルとリニアにしてんだけど、1.3と2点弾はなぜかコサインで6.7リニアで小さいBillonはコサインみたいな謎のことをしていることがわかると思う学習率を見てみると、6.0E-野木6.090-4乗だったのが一点に影っていうのマイナス4乗になってたりこの辺が変化してるということがわかると思います。これなんでかっていうと、学習したことは方だったら得られたのと、言語モデルに限らずだと思いますけど、あのモデルのサイズとかによって結構この辺の最適なパラメータ数ってのは変わるっていうのは何となくわかるかなと思ってます。左が何かある論文から持ってきた幅を変化させたときの学習率がどのぐらいだといいのか、最適なのかってのをプロットしたものになってます。色が幅に対応していて、横軸が学習率を6にしたものですね。これ見ると例えば8192の場合だと、このマイナス16ぐらいにいいとこいますし、128の場合だとマイナス10ぐらいということです。要は経験則としてモデルサイズを大きくしたとき、大きくごめんなさい。大きくなってるのが大きい大きくしたときには学生とちっちゃくするってのが大体だということがわかると思います。SalesGPTもそうなってますバッチサイズを大きくするといいってのも経験則としては言われているこの図はちょっと関係ないですけどそういう形で実際にはそのパラメータをスケールさせたりする必要があるということに注意してください。ちなみにこれある論文って言ったんすけどμTrasnformerGopherっていう論文でして、この論文は実はこのパラメータを変える必要があるってことを主張してるわけじゃなくて、何かいい感じの方法を使うといい感じってだいぶ大雑把に言いましたけど、この幅によらず最適なこの学習率の値バッチサイドもそうなんすけど、同じできるよってことを主張してる研究だったりします。ちょっと面白いので説明したいとこなんすけど興味ある人は、論文見てもらえばと思います何してるかっていうと簡単に言うと、れるじゃないや、あの重みの書記官ときとかに、入力の数とかに応じて初期化を変えると思うんですけど、それにいたことを学習率とか、努力のウエートにして掛けてやるとするとこれがウエイトに対して最適な学習率っていうのがこのケースだけであの最適になるということを示している研究だったりします。ちょっと興味ある人は読んでもらえばと思い、これm3の何か活用方法だけここに行ってますけど、これUPって書いてあるのが、このさっき言った怪しい怪しくはないんですけどただいい、いい感じの初期化をするということをしたものでしてそれやると学習室が変わらなくても、いいよってことがSalesGPTの場合だと言われています。ちなみにLlamaの場合はなんかちょっと論文見たんですけどちょっと厳密によくわかんなくてなんか参照したよっていうふうに書いてあるんすけど実際ラーニング例とは何かちょっといじってるみたいなので、この辺どうやってるかっていうの多分論文とか図形なモデルによると思うのでちょっとモデルを実際興味ある人は見てもらえばと思います。" 4 | }, 5 | { 6 | "content": "スケール則の話がここまでで終わりなのがトレーニングですけどそこの話終わりますけど、基本フィッティングすればいいんですけどモデルサイズを気にする方モデル再度スケールさせるときにはハイパーどうするかっていうところが、あの結構実験的にやられてまして、ボディサイズについては大体なんか幅アスペクト例集みたいなもの、ぱぱっと深さの比率みたいなものを維持しながら受けさせますし、学習率とかはスケジュール、明日への徐々に小さくする、大きくしたときに徐々に着するとか、ある意味Trasnformerという技術を使ってたりするということをご理解いただければと思います。" }, 7 | { 8 | "content": "講義に戻ります。ちょっと練習の時間もあるのであと20分ぐらいで駆け足になりますけど、最後最近のスケールトレンドって話で生のGENIACLMの話をして終わろうと思いですねちょっとモチベーションから話すと、ちょっと頭で考えてみてほしいとか見れば一瞬で思うとんですけどバナナの色は何ですかって言われたときと、今日の講義聞いた上で、ゲームソフトの問題は何だと思いますかって聞かれたとき、多分あの考えることが違うと思うんですね。羽の色なんですかっていうと一瞬黄色ですねもしかしたら緑かもしれないけどぐらいですかね物によるかなみたいなおもちゃだったら違うかもみたいな、だんだんあの、考えていくといろいろ出てくるかもしれないすけど、少なくともスケール足の問題なんだと思いますかって聞かれたときに、今日の話からするとスケール則っていうのはこういうものだからどうだろうこの辺が問題かなみたいな考えとやっぱ思考としては違うってことは何となく思うかなと思います。なんか人間的にはこの二つって全然違うしあの、答えるのに必要な考え方っていうのも違うように思えるわけです。スケールって言ってる7Gのスケールって言ってるのはこういった形で、あの簡単なものについては簡単に答えてもいいですし、そうじゃなくて、あの考えなきゃいけない問題に対しては、考える時間を、に計算式を使うというふうにしたときに、これいいことがあるのかっていうような話になってます。二つで、ちょっと順番が前後しますけどこれの仕組みは言語モデルでも効果的ですかっていう話と、これをどう実現できるかっていう、こういう二つの話が最近のトレンドとして出てきています。効果的ですかっていうのが、最近大湾と呼ばれる論文が論文じゃないか、モデルがオペルから出ましたプレビューとして出てますけどこの法案で注目されていますこれあの論文にROMってかブログにあるとイエスって右側が訓練時の計算資源をスケールさせたときに、初めて何かロジックのベンチマークがあるんですけどこれをがどうなったかで何となくスケールしてると右側がテストTimeコンピュートっていうふうに書いてると思うんすけど、水温時に計算資源を増やしたときあるモデルを使うんだけど、簡単に答える方法と深く考えて答える方法みたいでだんだんコース計算式を増やしていったときに、性能がどう変わるかっていうのでこれもスケールしていってるということがわかると思います。こういった形で、要は考える時間をどうやら推論時に使うと計算資源を推論使うのはいいことがありそうだということがわかります。" }, 9 | { 10 | "content": "そうすると次の話はどうやって計算資源をスケールするのか計算量スケールするのかって話ですけど実は一番簡単な方法はいくつかもうこの講義でも触れていて一つは例えばチェーン相当使うっていうのもその一つです。チャームソフトを使うと出力するプロtoken数が増えますよね。その思考の過程を自分で出力するので、これってのはある意味計算資源を増やして使っちゃってるとフェアに言うと、直接答えるのと、tokenをいっぱい出して使う。答えるのは違うわけですよね。ありていに言うとかかるお金が違うわけですからそれから目にちょっとICLっていうのも、あのDay2で多分少し話したのかなと思いますけどPretrainingときに学習テキストに入れるQを、サイズを増やしていくとどうなるかっていうのでこれもスケールしますよということが言われてましたけどこういった形で簡単にこのPromptingのときに出力するtokenだったり、入力するtokenっていうのをいじってやると、計算量を増やすことができるので、なんでこの辺の研究から見ても明らかなんですけど、本当に間うまく考えるのに計算式が使えれば性能が上がりますよってのは、既にあの子のあのSTまでにも話してた話だと思います。" 11 | }, 12 | { 13 | "content": "それからDay3でもDecodingっていう仕組みを、あの話したと思います。このDecodingにもいろんな方法があってグリーンDecodingだと単純に一番いいやつを選んでいく、一番確率が高いやつ選んでいくので、すごい単純ですけど、こういうトップKeyを取るとかトップ系を取るとかして、最後に一番いいやつを選ぶみたいなことをすると、これも結局計算をたくさんしてることになるわけですね。1個選ぶわけじゃないので、次に評価しなきゃいけないものがどんどん増えていくわけなので、こういった形で増やすっていうのも一つのやり方として存在しています。これ何かDecoding方法の一覧ですけどこれ興味ある人いたら言ってください。" }, 14 | { 15 | "content": "どうしようかな。時間も関係あるんでなんか最近の例を一つだけ紹介するとコントラスト美Decodingというのは例えばいまして、Xとリップスティックって書いてあるこの軸にそうとする方法の一つ外部のモデル別のモデルを使う方法なんですけどこの研究によると単純に自分が自分自身のこの出力の確率を使うよりも、なんかしょぼい言語モデルの確率を割ってやったものを使った方が、現地ちょっとログで言うと引いたものですね。方が良い指標になっているということが知られています。これなんか多分僕、なんかよくよく話しすぎちゃうのってよく出てくるものじゃないものについてエキスパートモデルが高く値を出していたLlama5161ってのは普通はあまり出さないわけですけどモデル出さないんだったら、このベースラインとして引いてあるっていうか終わってると良いよっていうようなDecoding方法も出てきています。ここで今つらくSteamDecoding詳細を理解してほしいということよりは、Decodingのときにその他のモデルを使って性能を上げると、要はこのアマチュアモデルの計算がさらに増えるわけですけど、こういった形の概念として新しい手法も出てきていたりしますということで、" }, 16 | { 17 | "content": "それからここまでの実行DINGの話は基本的に次のtokenをどう選ぶかという話をしてましたけど、Decoding、次のtokenをどう決めるかだけじゃなくて、全体プロセス全体としてどういうふうな生成を行うかっていうのを一段広く、維持した上から見ましょうというのでMetaGenerationという言い方をするような研究も出てきています。このfromDecodingMetaGenerationというのが、6月かな、に出たサーベイでして、理論人の言語モデルで使うアルゴリズムを広くサーベイしたものになってます。この衣装がMetaGenerationて会員になってますけどちょっとこれがどういうものかについて説明していきたいと思います。そうね三つぐらい種類があるっていうふうに書かれてますられるサービスSEPVersaceリファインメントですけどちょっとこれ何か説明より具体を見た方がわかりやすいと思うんで、それぞれ具体を説明していきたいと思います。最後にこれをずっと振り返ったこういうもんだなと思ってもらえばいいと思います。" }, 18 | { 19 | "content": "パレスサーチの方法の一番代表的なのがベストベールと呼ばれるものです。これ要は、これをtokenだと今までの話と同じなんすけど文を何個か生成してみて一番いいやつを選びましょうっていうのが、このパラレルサーチの一番簡単なベストWebっていう方法ですね。一番いいのっていうのがいろいろ考えられて、LLMのスコアを使うっていうのが、これがビール産地とかそういう普通の美味さとかに相当するものですけどベリファイと呼ばれるような学習した評価機を使うような研究だったり、GLUEとか機械翻訳のときにGLUEとか、特定の指標を使うみたいで何かの外部の指標を使って、とりあえずN構成して、一番いいやつを、後で選びましょうとそういうアプローチになってます。ちょっとMBRDecodingの話しようかと思うんすけど時間がだいぶ生BERTかもしれない簡単に言うとこれがMRDecodingというのが最近機械翻訳で注目されてる一つの方法らしいんですけど、この例の中で言うとGLUEとかの指標を使っていいものを選ぶというような研究の例になってます。そうですね伊藤ですけどこの何か期待値の中にいうて書いてあるけど、このUっていうのが、このさっきこのスライドでスコアと、相当してましてGLUEとかMetaとか、いろんなものを使っています。これは実際にこれ人間のサンプルがこういう交換するとこういうコンセプト必要なんですけど、これをこのMBRだとやめていって牝馬の代わりにモデルから生成したものをサンプリングして期待値を取るみたいなことをやるのがMBRDecodingというやれるやり方になってるんすけどちょっと詳細が知りたい人は、だいぶわかりやすい資料が上がってましたのでこのURLから飛んでもらえばと思います。ここではこの、要はスコアを選んでいいものを選ぶというようなやり方でいろいろ発展してきてるんだということを理解していただければいいと思います。" }, 20 | { 21 | "content": "それからBestBillonとはちょっと違う方法として、N個を生成した後に、それらを集約するという意味では、Day2Day2Falconシステムをこの枠組みの一つとして入り、あの説明されます。セル仕込ん試験紙は下のようなもんですけど推論のことを応用したものになってて、言語モデルにCoTでのいろんなニーズにパスを出させて、まじないずB'zパスって書いてあるけどそのリズムパス出した後にこの一番よく出てきた答えっていうのを最終的にお答えするというようなやり方になってると思います。これもN個の中から1個選んでるわけじゃないんですけどN個出力して、それを集約する形でこのNCoTはそれぞれ独立に動くのでパラレルに動くので、さらに動かして最後集約するという意味でこのペアパラレルサーチの枠組みの一つの代表的な手法になってます。このアグリゲーションどういうふうにこの結果を集約するか、すいません。だったり、どういうふうにスコアをつけるかっていうので、いろんな手法が知られてましてこれもさっきのサーベイ論文に入っているので興味を引いていけばてもらえればと思います。" }, 22 | { 23 | "content": "今のパラレルサーチの中でマジョリティーコーティングとベストWebっていうのが出てきましたけど、これあるタスクこれ確か米数学のタスクだと思うんすけどタスクが載ってないんで興味ある人はこの論文見てもらえばと思うんすけど、例えば比較すると、普通にマジョリティーコーティングするのがこの黒線で、あの青線がこのベストオブLMで終わるって書いてあるけど、これアウトカムSupervisedリワードモデルってもので要は全体に対して、これが正しいか正しくないかを推定するのリワードモデル結果を水の推計するヤードモデルってのを用意してあって、Value&モデルにとって一番良かったやつは最後選ぶっていうそういうやり方になってます。マジョリティーボディーはN個出して最大のものを選ぶっていうやり方です。これやると比べると、リワードモデル使った方がいいよということが反応だと言われています。一応補足なんすよGPTが別にそのリワードモデルとか作る必要ないので簡単な方法であるっていう利点はあって、ベスト弁の方のRMっていうのは、リワードモデル別で学習しなきゃいけないってのが欠点としてあることは一応補足しておきます。" }, 24 | { 25 | "content": "それからこのオレンジをスルーして話したんですけど実際この論文はこのオレンジのやり方を提案してる論文になってます。それがこのPRMっていうものでして、PRLってのはプロセスSupervisedリワードモデルっていうふうに呼ばれています。あるいは何かあの論文にあったプロセスリワードモデルって普通に読んでるものもあります。これは全体に対して合ってる間違ってるっていうのを予測するんじゃなくて、このプロセスごとに合ってる間違ってるみたいのを予測するモデルを作る。というものですこれあの数学の問題みたいなプロセスがわかりやすいですけど、最初にレッツコールTheair夢DXみたいな感じでそれぞれのバスが正しいか正しくないかみたいなこれはユーザーが提出してる例ですけど、ユーザーの提出した例を使ってプロセスの正しさを予測してるということをしています。これをやるとさらに性能が上がるっていうことが左の図からわかりまして、これちゃんと説明しなかったですけど横軸がN個なんで生成するものですね数になってますけど、これ増やしていくとさらに性能が上がるということがわかります。このPRみたいなプロセスに対して評価を付けるっていうな説明をしましたけど、こういったやり方をするのがステップレベルサーチっていうふうにさっきのサーベイLaMDAとまとめられています。ごめんなさい。この一つ前の研究ではこのプロセスLayerとモデルのキーワードを使って、あのテスト勉強を選ぶってやり方をしましたけど、ベスト以外にも、このプロセスごとに塊を作って生成していってビームサーチみたいなことをすることもできます。要は最初この2個が良さそうだからこのニコイチに怒らせて、この、こいつからまた発生させて2個作ってまた次のステップ3の選んでみたいな。これを繰り返すっていう方法です。これ丸が一つのプロセスtokenじゃなくてプロセスに対応してましてというのが普通のビルサーチの違いですけどそういった研究もあります。ちなみにそういった研究もありますって言いましたけどこの月Web外っていうのをDay2で多分やったと思うんすけどこれがほぼ似たようなことをしていますみたいなことっていうのはこのリワードモデルっていうの使う代わりに言語モデルで、この状態が戻るいいものかこっからこれゲームを24-0ですけど、達成24になる数字作り方をしてくださいっていうときに、あのもう絶対に達成できない場合は、あの言語モデルがフィールドするというようなことをしているものですけど、そういった形でやってるっていうのも、言語モデルをあのリワードの評価として使っているあの例だとステップレベルサーチのあの例だと、いただければと思います。これもさっきと同様ですけど探索方法とか検証するステップの違いですね。どこですっていう検証するかとかによっていろんな方法が知られています。FSでは先読みしてやるみたいなことです。これもちょっと時間の関係で全体を割愛しますけど、これもさBloomにあるのでやってみようかなと" }, 26 | { 27 | "content": "それから全然別のやり方でリファインメントと呼ばれるようなやり方もあります。これ概念図だけピックアップしましたけど、右側の黒いのが、なんかあの、対象の値ってこれを言語モデルが生成したものだとしたときに、これ自身を入れてやって、もっかい生成させるというようなことだったり、あるいはこの生成したものに対してフィードバックを、右側の左側の広い広いボックスに相当しますけど与えてやって再度生成するみたいな、そういったやり方をする研究もありますこれがリファインメントというようなやり方になってます。このリファイベントの代表的な研究がセルフリファインと言われるような研究がありまして左が概念図でさっきとほぼ同じですけど、なんか最初タスクを言語モデルに与えましたとこいつが出力した結果をあのモデル自分自身のこのセリフは研究だと自分自身で評価して、あとフィードバックを返すとそのフィードバックを行った結果を使ってもっかいリファインすると、このリファもこのセリフに入ると自分自身でやるんすけど、そういった枠組みの研究があります。右側例えば政令で上側のABCが一つの系列になってますけど、ダイアログが与えられたと会話が与えられたときに、このフィードバックって書いてあるのが、この左側の①のプロセスに相当するこれも言語モデルが出してるものでこのフィードバックを踏まえてリファインしたのがこの右側のものになります。ちょっと中身は呼ばないですけどこういった形で最初に生成させそれ何を変えるべきかっていうのも言語モデル生成して、最後にその何を変えるべきかというのを踏まえても改正するとこれを何回も何回も繰り返すっていうのはやり方になってます。こんなんで性能上がるのかって思うと思うんですけどこれが何か上がるらしくてですね、最大50パーぐらい上がるような例もあるっていうふうに言われています。結果は結果なので、一旦これぐらいしか" }, 28 | { 29 | "content": "これでほぼちょうどですけど、最後に少しあの、前半では全体の訓練時のスケーリングをする話を基本的にしましたけど、最近ではこの推論値の計算量っていうのも注目するような研究が増えてきています。代表的なGPT法案とかですごく注目されてるかなと思いますし、今までやった方法までを学んだ方法も結構出てきたと思いますけど、Promptingを工夫するとか、Decodingを工夫するとかいうので、それにも発展的な方法がいろいろ出てきていますし、まめちMetaGenerationっていうような枠組みで、Decodingだけじゃなくてそのレコードした結果を最後どう使うかみたいな含めて、GENERATIONSMetaGenerationというふうに呼んでますけど、ただヴェルサーチとかステップでVersaceとかリファインメントと言われるような枠組みの研究も出てきていますというような話をしました。" }, 30 | { 31 | "content": "最後に二つ補足しておくのパートだろうと思いますけど同じ計算資源のときにパラメータ増やすのよりの推論資源を増やすのが有効なのかっていうのが問いとしてあると思いますけどオープンあの場合だと、訓練時のスケールは同じままっていうのちょっとスケールを増やしたら、より賢くなりましたって話でしたけど、どっちにするのがいいのかっていう意味で言うと、GoogleDeepMindか。1月に論文としてまして、スケーリングLLMthisTimeコンピュート口真理ちゃん日は増えてるっていうことで、良いらしいというふうに言われてます。厳密に言うとこれなんかタスクによって違うということなので、良いとまで言っていいのかちょっと若干誇大広告な気が個人的にはしてますけどそういったことを検証するような研究も出てきていますので興味ある人は見てもらえばと思います。" } 32 | ] -------------------------------------------------------------------------------- /day5/.gitignore: -------------------------------------------------------------------------------- 1 | mlruns 2 | 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | share/python-wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | MANIFEST 31 | 32 | # PyInstaller 33 | # Usually these files are written by a python script from a template 34 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 35 | *.manifest 36 | *.spec 37 | 38 | # Installer logs 39 | pip-log.txt 40 | pip-delete-this-directory.txt 41 | 42 | # Unit test / coverage reports 43 | htmlcov/ 44 | .tox/ 45 | .nox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *.cover 52 | *.py,cover 53 | .hypothesis/ 54 | .pytest_cache/ 55 | cover/ 56 | 57 | # Translations 58 | *.mo 59 | *.pot 60 | 61 | # Django stuff: 62 | *.log 63 | local_settings.py 64 | db.sqlite3 65 | db.sqlite3-journal 66 | 67 | # Flask stuff: 68 | instance/ 69 | .webassets-cache 70 | 71 | # Scrapy stuff: 72 | .scrapy 73 | 74 | # Sphinx documentation 75 | docs/_build/ 76 | 77 | # PyBuilder 78 | .pybuilder/ 79 | target/ 80 | 81 | # Jupyter Notebook 82 | .ipynb_checkpoints 83 | 84 | # IPython 85 | profile_default/ 86 | ipython_config.py 87 | 88 | # pyenv 89 | # For a library or package, you might want to ignore these files since the code is 90 | # intended to run in multiple environments; otherwise, check them in: 91 | # .python-version 92 | 93 | # pipenv 94 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 95 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 96 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 97 | # install all needed dependencies. 98 | #Pipfile.lock 99 | 100 | # UV 101 | # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control. 102 | # This is especially recommended for binary packages to ensure reproducibility, and is more 103 | # commonly ignored for libraries. 104 | #uv.lock 105 | 106 | # poetry 107 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 108 | # This is especially recommended for binary packages to ensure reproducibility, and is more 109 | # commonly ignored for libraries. 110 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 111 | #poetry.lock 112 | 113 | # pdm 114 | # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. 115 | #pdm.lock 116 | # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it 117 | # in version control. 118 | # https://pdm.fming.dev/latest/usage/project/#working-with-version-control 119 | .pdm.toml 120 | .pdm-python 121 | .pdm-build/ 122 | 123 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm 124 | __pypackages__/ 125 | 126 | # Celery stuff 127 | celerybeat-schedule 128 | celerybeat.pid 129 | 130 | # SageMath parsed files 131 | *.sage.py 132 | 133 | # Environments 134 | .env 135 | .venv 136 | env/ 137 | venv/ 138 | ENV/ 139 | env.bak/ 140 | venv.bak/ 141 | 142 | # Spyder project settings 143 | .spyderproject 144 | .spyproject 145 | 146 | # Rope project settings 147 | .ropeproject 148 | 149 | # mkdocs documentation 150 | /site 151 | 152 | # mypy 153 | .mypy_cache/ 154 | .dmypy.json 155 | dmypy.json 156 | 157 | # Pyre type checker 158 | .pyre/ 159 | 160 | # pytype static type analyzer 161 | .pytype/ 162 | 163 | # Cython debug symbols 164 | cython_debug/ 165 | 166 | # PyCharm 167 | # JetBrains specific template is maintained in a separate JetBrains.gitignore that can 168 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 169 | # and can be added to the global gitignore or merged into this file. For a more nuclear 170 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 171 | #.idea/ 172 | 173 | # Ruff stuff: 174 | .ruff_cache/ 175 | 176 | # PyPI configuration file 177 | .pypirc 178 | -------------------------------------------------------------------------------- /day5/README.md: -------------------------------------------------------------------------------- 1 | # day5 演習用ディレクトリ 2 | 3 | 第3回「MLOps」に関する演習用のディレクトリです。 4 | 5 | # 演習の目的 6 | 7 | 本演習コンテンツでは、技術的な用語や仕組み(例:高度な実験管理や複雑なパイプライン処理など)の詳細な理解を目的とするのではなく、モデルの管理やAIシステムを継続的に取り扱うための方法を実際に体験することを主眼としています。 8 | 9 | この体験を通じて、AIを継続的に扱うための仕組みや考え方を学び、実践的なスキルへの理解を深めることを目的とします。 10 | 11 | # 演習概要 12 | 主な演習の流れは以下のようになっています。 13 | 14 | 1. 機械学習モデルの実験管理とパイプライン 15 | 2. 整合性テスト 16 | 3. CI(継続的インテクレーション) 17 | 18 | 本演習では、GPUを使用しないため、実行環境として「Google Colab」を前提としません。ご自身のPC等でPythonの実行環境を用意していただき、演習を行なっていただいて問題ありません。 19 | 20 | # 事前準備 21 | GitHubを使用した演習となるため、GitHubの「fork」、「clone」、「pull request」などの操作を行います。 22 | GitHubをご自身の演習環境上で使用できるようにしておいてください。 23 | 24 | 「fork」、「clone」したスクリプトに対してgitコマンドで構成管理を行います。 25 | また、講師の環境ではGitHub CLIを使用する場合があります。 26 | 必須ではありませんが、GitHub CLIを使えるようにしておくとGitHubの操作が楽に行える場合があります。 27 | 28 | - GitHub CLIについて 29 | [https://docs.github.com/ja/github-cli/github-cli/about-github-cli](https://docs.github.com/ja/github-cli/github-cli/about-github-cli) 30 | 31 | ## 環境を用意できない方向け 32 | 「Google Colab」上でも演習を行うことは可能です。 33 | 演習内でMLflowというソフトウェアを使いUIを表示する際に、Google Colab上ではngrokを使用することになります。 34 | 35 | GitHubを「Google Colab」上で使用できるようにするのに加え、day1の演習で使用したngrokを使用してMLflowのUIを表示しましょう。 36 | 37 | ### 参考情報 38 | - Google Colab上でGitHubを使う 39 | [https://zenn.dev/smiyawaki0820/articles/e346ca8b522356](https://zenn.dev/smiyawaki0820/articles/e346ca8b522356) 40 | - Google Colab上でMLflow UIを使う 41 | [https://fight-tsk.blogspot.com/2021/07/mlflow-ui-google-colaboratory.html](https://fight-tsk.blogspot.com/2021/07/mlflow-ui-google-colaboratory.html) 42 | 43 | # 演習内容 44 | 演習は大きく3つのパートに分かれています。 45 | - 演習1: 機械学習モデルの実験管理とパイプライン 46 | - 演習2: 整合性テスト 47 | - 演習3: CI(継続的インテクレーション) 48 | 49 | 演習1と演習2は、day5フォルダ直下の「演習1」と「演習2」フォルダを使用します。 50 | 51 | 演習3は、リポジトリ直下の「.github」フォルダと、day5フォルダ直下の「演習3」フォルダを使用します。 52 | 53 | 演習に必要なライブラリは、day5フォルダ直下の「requirements.txt」ファイルに記載しています。各自の演習環境にインストールしてください。 54 | 55 | ## 演習1: 機械学習モデルの実験管理とパイプライン 56 | 57 | ### ゴール 58 | - scikit-learn + MLflow + kedro を使用して、学習 → 評価 → モデル保存までのパイプラインを構築。 59 | - パイプラインを動かす。 60 | 61 | ### 演習ステップ 62 | 1. **データ準備** 63 | - Titanic データを使用 64 | - データの取得 → 学習用・評価用に分割 65 | 66 | 2. **学習スクリプトの実装** 67 | - ランダムフォレストによる学習処理 68 | - 評価処理(accuracy)を関数化して構成 69 | 70 | 3. **モデル保存** 71 | - MLflow を用いて学習済みモデルをトラッキング・保存 72 | 73 | 4. **オプション** 74 | - MLflow UI による可視化 75 | - パラメータ変更や再実行による再現性の確認・テスト 76 | 77 | 5. **パイプライン化** 78 | - kedro を使って処理を Node 化して組み立て 79 | 80 | #### 演習1で使用する主なコマンド 81 | ```bash 82 | cd 演習1 83 | 84 | python main.py 85 | mlflow ui 86 | 87 | python pipeline.py 88 | ``` 89 | 90 | --- 91 | 92 | ## 演習2: 整合性テスト 93 | 94 | ### ゴール 95 | - データの整合性チェック 96 | - モデルの動作テスト 97 | 98 | ### 演習ステップ 99 | 1. **データテスト** 100 | - pytest + great_expectations を使用 101 | - データの型・欠損・値範囲を検証 102 | 103 | 2. **フォーマットチェック** 104 | - black, flake8, などで静的コードチェックを実施 105 | 106 | #### 演習2で使用する主なコマンド 107 | ```bash 108 | cd 演習2 109 | 110 | python main.py 111 | pytest main.py 112 | 113 | black main.py 114 | ``` 115 | 116 | ## 演習3: CI(継続的インテクレーション) 117 | 118 | ### ゴール 119 | - Python コードの静的検査・ユニットテストを含む CI 構築 120 | 121 | 1. **CI ツール導入** 122 | - GitHub Actions を使用 123 | - `.github/workflows/test.yml` を作成 124 | 125 | 1. **CI 結果確認** 126 | - プルリクエスト時に自動でチェックを実行 127 | 128 | #### 演習3で使用する主なコマンド 129 | GitHub CLIを使用した場合のプルリクエストの流れ 130 | 131 | ```bash 132 | git branch develop 133 | git checkout develop 134 | gh repo set-default 135 | gh pr create 136 | ``` 137 | 138 | # 宿題の関連情報 139 | ## CIでのテストケースを追加する場合のアイディアサンプル 140 | 141 | 1. **モデルテスト** 142 | - モデルの推論精度(再現性)や推論時間を検証 143 | 144 | 2. **差分テスト** 145 | - 過去バージョンと比較して性能の劣化がないか確認 146 | - 例: `accuracy ≥ baseline` 147 | -------------------------------------------------------------------------------- /day5/requirements.txt: -------------------------------------------------------------------------------- 1 | kedro 2 | scikit-learn 3 | mlflow 4 | pandas 5 | pytest 6 | great_expectations 7 | black -------------------------------------------------------------------------------- /day5/演習1/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import mlflow 3 | import mlflow.sklearn 4 | import pandas as pd 5 | import numpy as np 6 | import random 7 | import pickle 8 | from sklearn.model_selection import train_test_split 9 | from sklearn.ensemble import RandomForestClassifier 10 | from sklearn.metrics import accuracy_score 11 | from sklearn.preprocessing import LabelEncoder 12 | from mlflow.models.signature import infer_signature 13 | 14 | 15 | # データ準備 16 | def prepare_data(test_size=0.2, random_state=42): 17 | # Titanicデータセットの読み込み 18 | path = "data/Titanic.csv" 19 | data = pd.read_csv(path) 20 | 21 | # 必要な特徴量の選択と前処理 22 | data = data[["Pclass", "Sex", "Age", "Fare", "Survived"]].dropna() 23 | data["Sex"] = LabelEncoder().fit_transform(data["Sex"]) # 性別を数値に変換 24 | 25 | # 整数型の列を浮動小数点型に変換 26 | data["Pclass"] = data["Pclass"].astype(float) 27 | data["Sex"] = data["Sex"].astype(float) 28 | data["Age"] = data["Age"].astype(float) 29 | data["Fare"] = data["Fare"].astype(float) 30 | data["Survived"] = data["Survived"].astype(float) 31 | 32 | X = data[["Pclass", "Sex", "Age", "Fare"]] 33 | y = data["Survived"] 34 | 35 | # データ分割 36 | X_train, X_test, y_train, y_test = train_test_split( 37 | X, y, test_size=test_size, random_state=random_state 38 | ) 39 | return X_train, X_test, y_train, y_test 40 | 41 | 42 | # 学習と評価 43 | def train_and_evaluate( 44 | X_train, X_test, y_train, y_test, n_estimators=100, max_depth=None, random_state=42 45 | ): 46 | model = RandomForestClassifier( 47 | n_estimators=n_estimators, max_depth=max_depth, random_state=random_state 48 | ) 49 | model.fit(X_train, y_train) 50 | predictions = model.predict(X_test) 51 | accuracy = accuracy_score(y_test, predictions) 52 | return model, accuracy 53 | 54 | 55 | # モデル保存 56 | def log_model(model, accuracy, params): 57 | with mlflow.start_run(): 58 | # パラメータをログ 59 | for param_name, param_value in params.items(): 60 | mlflow.log_param(param_name, param_value) 61 | 62 | # メトリクスをログ 63 | mlflow.log_metric("accuracy", accuracy) 64 | 65 | # モデルのシグネチャを推論 66 | signature = infer_signature(X_train, model.predict(X_train)) 67 | 68 | # モデルを保存 69 | mlflow.sklearn.log_model( 70 | model, 71 | "model", 72 | signature=signature, 73 | input_example=X_test.iloc[:5], # 入力例を指定 74 | ) 75 | # accurecyとparmsは改行して表示 76 | print(f"モデルのログ記録値 \naccuracy: {accuracy}\nparams: {params}") 77 | 78 | 79 | # メイン処理 80 | if __name__ == "__main__": 81 | # ランダム要素の設定 82 | test_size = round( 83 | random.uniform(0.1, 0.3), 2 84 | ) # 10%〜30%の範囲でテストサイズをランダム化 85 | data_random_state = random.randint(1, 100) 86 | model_random_state = random.randint(1, 100) 87 | n_estimators = random.randint(50, 200) 88 | max_depth = random.choice([None, 3, 5, 10, 15]) 89 | 90 | # パラメータ辞書の作成 91 | params = { 92 | "test_size": test_size, 93 | "data_random_state": data_random_state, 94 | "model_random_state": model_random_state, 95 | "n_estimators": n_estimators, 96 | "max_depth": "None" if max_depth is None else max_depth, 97 | } 98 | 99 | # データ準備 100 | X_train, X_test, y_train, y_test = prepare_data( 101 | test_size=test_size, random_state=data_random_state 102 | ) 103 | 104 | # 学習と評価 105 | model, accuracy = train_and_evaluate( 106 | X_train, 107 | X_test, 108 | y_train, 109 | y_test, 110 | n_estimators=n_estimators, 111 | max_depth=max_depth, 112 | random_state=model_random_state, 113 | ) 114 | 115 | # モデル保存 116 | log_model(model, accuracy, params) 117 | 118 | model_dir = "models" 119 | os.makedirs(model_dir, exist_ok=True) 120 | model_path = os.path.join(model_dir, f"titanic_model.pkl") 121 | with open(model_path, "wb") as f: 122 | pickle.dump(model, f) 123 | print(f"モデルを {model_path} に保存しました") 124 | -------------------------------------------------------------------------------- /day5/演習1/models/titanic_model.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matsuolab/lecture-ai-engineering/7950e32c72612d06c80521fc2bbdf5f6df9c9db2/day5/演習1/models/titanic_model.pkl -------------------------------------------------------------------------------- /day5/演習1/pipeline.py: -------------------------------------------------------------------------------- 1 | from kedro.io import MemoryDataset, KedroDataCatalog 2 | from kedro.pipeline import Pipeline, node 3 | from kedro.runner import SequentialRunner 4 | from sklearn.model_selection import train_test_split 5 | from sklearn.ensemble import RandomForestClassifier 6 | from sklearn.metrics import accuracy_score 7 | from sklearn.preprocessing import LabelEncoder 8 | import pandas as pd 9 | import mlflow 10 | import mlflow.sklearn 11 | from mlflow.models.signature import infer_signature 12 | import os 13 | import random 14 | import logging 15 | 16 | # ロガーの設定 17 | logging.basicConfig( 18 | level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s" 19 | ) 20 | logger = logging.getLogger(__name__) 21 | 22 | 23 | # データ準備 24 | def prepare_data(): 25 | try: 26 | # Titanicデータセットの読み込み 27 | path = "data/Titanic.csv" 28 | if not os.path.exists(path): 29 | raise FileNotFoundError(f"データファイルが見つかりません: {path}") 30 | 31 | data = pd.read_csv(path) 32 | logger.info(f"データを読み込みました。行数: {len(data)}") 33 | 34 | # 必要な特徴量の選択と前処理 35 | data = data[["Pclass", "Sex", "Age", "Fare", "Survived"]].dropna() 36 | logger.info(f"欠損値削除後の行数: {len(data)}") 37 | 38 | data["Sex"] = LabelEncoder().fit_transform(data["Sex"]) # 性別を数値に変換 39 | 40 | # 整数型の列を浮動小数点型に変換 41 | data["Pclass"] = data["Pclass"].astype(float) 42 | data["Sex"] = data["Sex"].astype(float) 43 | data["Age"] = data["Age"].astype(float) 44 | data["Fare"] = data["Fare"].astype(float) 45 | data["Survived"] = data["Survived"].astype(float) 46 | 47 | X = data[["Pclass", "Sex", "Age", "Fare"]] 48 | y = data["Survived"] 49 | 50 | # データ分割 51 | X_train, X_test, y_train, y_test = train_test_split( 52 | X, y, test_size=0.2, random_state=42 53 | ) 54 | logger.info( 55 | f"トレーニングデータ: {X_train.shape}, テストデータ: {X_test.shape}" 56 | ) 57 | return X_train, X_test, y_train, y_test 58 | except Exception as e: 59 | logger.error(f"データ準備中にエラーが発生しました: {str(e)}") 60 | raise 61 | 62 | 63 | # 学習と評価 64 | def train_and_evaluate(X_train, X_test, y_train, y_test): 65 | try: 66 | # ハイパーパラメータの設定 67 | params = { 68 | "n_estimators": random.randint(50, 200), 69 | "max_depth": random.choice([None, 3, 5, 10, 15]), 70 | "min_samples_split": 2, 71 | "random_state": 42, 72 | } 73 | 74 | model = RandomForestClassifier(**params) 75 | model.fit(X_train, y_train) 76 | predictions = model.predict(X_test) 77 | accuracy = accuracy_score(y_test, predictions) 78 | logger.info(f"モデルの精度: {accuracy:.4f}") 79 | return model, accuracy, params 80 | except Exception as e: 81 | logger.error(f"モデル学習中にエラーが発生しました: {str(e)}") 82 | raise 83 | 84 | 85 | # モデル保存 86 | def log_model(model, accuracy, params, X_train, X_test): 87 | try: 88 | # 実験名の設定 89 | mlflow.set_experiment("titanic-survival-prediction") 90 | 91 | with mlflow.start_run(): 92 | # メトリクスのロギング 93 | mlflow.log_metric("accuracy", accuracy) 94 | 95 | # ハイパーパラメータのロギング 96 | mlflow.log_params(params) 97 | 98 | # 重要な特徴量のロギング 99 | feature_importances = model.feature_importances_ 100 | for i, feature in enumerate(X_train.columns): 101 | mlflow.log_metric( 102 | f"feature_importance_{feature}", feature_importances[i] 103 | ) 104 | 105 | # モデルのシグネチャを推論 106 | signature = infer_signature(X_train, model.predict(X_train)) 107 | 108 | # モデルを保存 109 | mlflow.sklearn.log_model( 110 | model, 111 | "model", 112 | signature=signature, 113 | input_example=X_test.iloc[:5], # 入力例を指定 114 | ) 115 | 116 | # アーティファクトの場所を取得してログに出力 117 | run_id = mlflow.active_run().info.run_id 118 | logger.info(f"モデルを記録しました。Run ID: {run_id}") 119 | logger.info(f"精度: {accuracy:.4f}") 120 | except Exception as e: 121 | logger.error(f"MLflowでのモデル記録中にエラーが発生しました: {str(e)}") 122 | raise 123 | 124 | 125 | # Kedro パイプラインの定義 126 | def create_pipeline(): 127 | return Pipeline( 128 | [ 129 | node( 130 | prepare_data, 131 | inputs=None, 132 | outputs=["X_train", "X_test", "y_train", "y_test"], 133 | name="prepare_data", 134 | ), 135 | node( 136 | train_and_evaluate, 137 | inputs=["X_train", "X_test", "y_train", "y_test"], 138 | outputs=["model", "accuracy", "params"], 139 | name="train_and_evaluate", 140 | ), 141 | node( 142 | log_model, 143 | inputs=["model", "accuracy", "params", "X_train", "X_test"], 144 | outputs=None, 145 | name="log_model", 146 | ), 147 | ] 148 | ) 149 | 150 | 151 | if __name__ == "__main__": 152 | try: 153 | # パイプラインの作成 154 | pipeline = create_pipeline() 155 | 156 | # データカタログの作成 157 | catalog = KedroDataCatalog( 158 | { 159 | "X_train": MemoryDataset(), 160 | "X_test": MemoryDataset(), 161 | "y_train": MemoryDataset(), 162 | "y_test": MemoryDataset(), 163 | "model": MemoryDataset(), 164 | "accuracy": MemoryDataset(), 165 | "params": MemoryDataset(), 166 | } 167 | ) 168 | 169 | # Kedro ランナーの作成 170 | runner = SequentialRunner() 171 | 172 | # パイプラインの実行 173 | logger.info("パイプラインの実行を開始します。") 174 | runner.run(pipeline, catalog) 175 | logger.info("パイプラインの実行が完了しました。") 176 | except Exception as e: 177 | logger.error(f"パイプラインの実行中にエラーが発生しました: {str(e)}") 178 | -------------------------------------------------------------------------------- /day5/演習2/black_check.py: -------------------------------------------------------------------------------- 1 | 2 | def say_hello(name):print("Hello,"+name+"!") # greet 3 | def say_hello(name):print("Hello," + name +"!") # greet 4 | def add( a,b):return a+b 5 | def add( a , b ):return a+b 6 | def add(a, b): 7 | return a+b -------------------------------------------------------------------------------- /day5/演習2/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pandas as pd 3 | from sklearn.ensemble import RandomForestClassifier 4 | from sklearn.model_selection import train_test_split 5 | from sklearn.metrics import accuracy_score 6 | from sklearn.preprocessing import StandardScaler, OneHotEncoder 7 | from sklearn.compose import ColumnTransformer 8 | from sklearn.pipeline import Pipeline 9 | from sklearn.impute import SimpleImputer 10 | import pickle 11 | import time 12 | import great_expectations as gx 13 | 14 | class DataLoader: 15 | """データロードを行うクラス""" 16 | 17 | @staticmethod 18 | def load_titanic_data(path=None): 19 | """Titanicデータセットを読み込む""" 20 | if path: 21 | return pd.read_csv(path) 22 | else: 23 | # ローカルのファイル 24 | local_path = "data/Titanic.csv" 25 | if os.path.exists(local_path): 26 | return pd.read_csv(local_path) 27 | 28 | @staticmethod 29 | def preprocess_titanic_data(data): 30 | """Titanicデータを前処理する""" 31 | # 必要な特徴量を選択 32 | data = data.copy() 33 | 34 | # 不要な列を削除 35 | columns_to_drop = [] 36 | for col in ["PassengerId", "Name", "Ticket", "Cabin"]: 37 | if col in data.columns: 38 | columns_to_drop.append(col) 39 | 40 | if columns_to_drop: 41 | data.drop(columns_to_drop, axis=1, inplace=True) 42 | 43 | # 目的変数とその他を分離 44 | if "Survived" in data.columns: 45 | y = data["Survived"] 46 | X = data.drop("Survived", axis=1) 47 | return X, y 48 | else: 49 | return data, None 50 | 51 | 52 | class DataValidator: 53 | """データバリデーションを行うクラス""" 54 | 55 | @staticmethod 56 | def validate_titanic_data(data): 57 | """Titanicデータセットの検証""" 58 | # DataFrameに変換 59 | if not isinstance(data, pd.DataFrame): 60 | return False, ["データはpd.DataFrameである必要があります"] 61 | 62 | # Great Expectationsを使用したバリデーション 63 | try: 64 | context = gx.get_context() 65 | data_source = context.data_sources.add_pandas("pandas") 66 | data_asset = data_source.add_dataframe_asset(name="pd dataframe asset") 67 | 68 | batch_definition = data_asset.add_batch_definition_whole_dataframe( 69 | "batch definition" 70 | ) 71 | batch = batch_definition.get_batch(batch_parameters={"dataframe": data}) 72 | 73 | results = [] 74 | 75 | # 必須カラムの存在確認 76 | required_columns = [ 77 | "Pclass", 78 | "Sex", 79 | "Age", 80 | "SibSp", 81 | "Parch", 82 | "Fare", 83 | "Embarked", 84 | ] 85 | missing_columns = [ 86 | col for col in required_columns if col not in data.columns 87 | ] 88 | if missing_columns: 89 | print(f"警告: 以下のカラムがありません: {missing_columns}") 90 | return False, [{"success": False, "missing_columns": missing_columns}] 91 | 92 | expectations = [ 93 | gx.expectations.ExpectColumnDistinctValuesToBeInSet( 94 | column="Pclass", value_set=[1, 2, 3] 95 | ), 96 | gx.expectations.ExpectColumnDistinctValuesToBeInSet( 97 | column="Sex", value_set=["male", "female"] 98 | ), 99 | gx.expectations.ExpectColumnValuesToBeBetween( 100 | column="Age", min_value=0, max_value=100 101 | ), 102 | gx.expectations.ExpectColumnValuesToBeBetween( 103 | column="Fare", min_value=0, max_value=600 104 | ), 105 | gx.expectations.ExpectColumnDistinctValuesToBeInSet( 106 | column="Embarked", value_set=["C", "Q", "S", ""] 107 | ), 108 | ] 109 | 110 | for expectation in expectations: 111 | result = batch.validate(expectation) 112 | results.append(result) 113 | 114 | # すべての検証が成功したかチェック 115 | is_successful = all(result.success for result in results) 116 | return is_successful, results 117 | 118 | except Exception as e: 119 | print(f"Great Expectations検証エラー: {e}") 120 | return False, [{"success": False, "error": str(e)}] 121 | 122 | 123 | class ModelTester: 124 | """モデルテストを行うクラス""" 125 | 126 | @staticmethod 127 | def create_preprocessing_pipeline(): 128 | """前処理パイプラインを作成""" 129 | numeric_features = ["Age", "Fare", "SibSp", "Parch"] 130 | numeric_transformer = Pipeline( 131 | steps=[ 132 | ("imputer", SimpleImputer(strategy="median")), 133 | ("scaler", StandardScaler()), 134 | ] 135 | ) 136 | 137 | categorical_features = ["Pclass", "Sex", "Embarked"] 138 | categorical_transformer = Pipeline( 139 | steps=[ 140 | ("imputer", SimpleImputer(strategy="most_frequent")), 141 | ("onehot", OneHotEncoder(handle_unknown="ignore")), 142 | ] 143 | ) 144 | 145 | preprocessor = ColumnTransformer( 146 | transformers=[ 147 | ("num", numeric_transformer, numeric_features), 148 | ("cat", categorical_transformer, categorical_features), 149 | ], 150 | remainder="drop", # 指定されていない列は削除 151 | ) 152 | return preprocessor 153 | 154 | @staticmethod 155 | def train_model(X_train, y_train, model_params=None): 156 | """モデルを学習する""" 157 | if model_params is None: 158 | model_params = {"n_estimators": 100, "random_state": 42} 159 | 160 | # 前処理パイプラインを作成 161 | preprocessor = ModelTester.create_preprocessing_pipeline() 162 | 163 | # モデル作成 164 | model = Pipeline( 165 | steps=[ 166 | ("preprocessor", preprocessor), 167 | ("classifier", RandomForestClassifier(**model_params)), 168 | ] 169 | ) 170 | 171 | # 学習 172 | model.fit(X_train, y_train) 173 | return model 174 | 175 | @staticmethod 176 | def evaluate_model(model, X_test, y_test): 177 | """モデルを評価する""" 178 | start_time = time.time() 179 | y_pred = model.predict(X_test) 180 | inference_time = time.time() - start_time 181 | 182 | accuracy = accuracy_score(y_test, y_pred) 183 | return {"accuracy": accuracy, "inference_time": inference_time} 184 | 185 | @staticmethod 186 | def save_model(model, path="models/titanic_model.pkl"): 187 | model_dir = "models" 188 | os.makedirs(model_dir, exist_ok=True) 189 | model_path = os.path.join(model_dir, f"titanic_model.pkl") 190 | with open(model_path, "wb") as f: 191 | pickle.dump(model, f) 192 | return path 193 | 194 | @staticmethod 195 | def load_model(path="models/titanic_model.pkl"): 196 | """モデルを読み込む""" 197 | with open(path, "rb") as f: 198 | model = pickle.load(f) 199 | return model 200 | 201 | @staticmethod 202 | def compare_with_baseline(current_metrics, baseline_threshold=0.75): 203 | """ベースラインと比較する""" 204 | return current_metrics["accuracy"] >= baseline_threshold 205 | 206 | 207 | # テスト関数(pytestで実行可能) 208 | def test_data_validation(): 209 | """データバリデーションのテスト""" 210 | # データロード 211 | data = DataLoader.load_titanic_data() 212 | X, y = DataLoader.preprocess_titanic_data(data) 213 | 214 | # 正常なデータのチェック 215 | success, results = DataValidator.validate_titanic_data(X) 216 | assert success, "データバリデーションに失敗しました" 217 | 218 | # 異常データのチェック 219 | bad_data = X.copy() 220 | bad_data.loc[0, "Pclass"] = 5 # 明らかに範囲外の値 221 | success, results = DataValidator.validate_titanic_data(bad_data) 222 | assert not success, "異常データをチェックできませんでした" 223 | 224 | 225 | def test_model_performance(): 226 | """モデル性能のテスト""" 227 | # データ準備 228 | data = DataLoader.load_titanic_data() 229 | X, y = DataLoader.preprocess_titanic_data(data) 230 | X_train, X_test, y_train, y_test = train_test_split( 231 | X, y, test_size=0.2, random_state=42 232 | ) 233 | 234 | # モデル学習 235 | model = ModelTester.train_model(X_train, y_train) 236 | 237 | # 評価 238 | metrics = ModelTester.evaluate_model(model, X_test, y_test) 239 | 240 | # ベースラインとの比較 241 | assert ModelTester.compare_with_baseline( 242 | metrics, 0.75 243 | ), f"モデル性能がベースラインを下回っています: {metrics['accuracy']}" 244 | 245 | # 推論時間の確認 246 | assert ( 247 | metrics["inference_time"] < 1.0 248 | ), f"推論時間が長すぎます: {metrics['inference_time']}秒" 249 | 250 | 251 | if __name__ == "__main__": 252 | # データロード 253 | data = DataLoader.load_titanic_data() 254 | X, y = DataLoader.preprocess_titanic_data(data) 255 | 256 | # データバリデーション 257 | success, results = DataValidator.validate_titanic_data(X) 258 | print(f"データ検証結果: {'成功' if success else '失敗'}") 259 | for result in results: 260 | # "success": falseの場合はエラーメッセージを表示 261 | if not result["success"]: 262 | print(f"異常タイプ: {result['expectation_config']['type']}, 結果: {result}") 263 | if not success: 264 | print("データ検証に失敗しました。処理を終了します。") 265 | exit(1) 266 | 267 | # モデルのトレーニングと評価 268 | X_train, X_test, y_train, y_test = train_test_split( 269 | X, y, test_size=0.2, random_state=42 270 | ) 271 | 272 | # パラメータ設定 273 | model_params = {"n_estimators": 100, "random_state": 42} 274 | 275 | # モデルトレーニング 276 | model = ModelTester.train_model(X_train, y_train, model_params) 277 | metrics = ModelTester.evaluate_model(model, X_test, y_test) 278 | 279 | print(f"精度: {metrics['accuracy']:.4f}") 280 | print(f"推論時間: {metrics['inference_time']:.4f}秒") 281 | 282 | # モデル保存 283 | model_path = ModelTester.save_model(model) 284 | 285 | # ベースラインとの比較 286 | baseline_ok = ModelTester.compare_with_baseline(metrics) 287 | print(f"ベースライン比較: {'合格' if baseline_ok else '不合格'}") 288 | -------------------------------------------------------------------------------- /day5/演習2/models/titanic_model.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matsuolab/lecture-ai-engineering/7950e32c72612d06c80521fc2bbdf5f6df9c9db2/day5/演習2/models/titanic_model.pkl -------------------------------------------------------------------------------- /day5/演習3/models/titanic_model.pkl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matsuolab/lecture-ai-engineering/7950e32c72612d06c80521fc2bbdf5f6df9c9db2/day5/演習3/models/titanic_model.pkl -------------------------------------------------------------------------------- /day5/演習3/tests/test_data.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | import pandas as pd 4 | import numpy as np 5 | import great_expectations as gx 6 | from sklearn.datasets import fetch_openml 7 | import warnings 8 | 9 | # 警告を抑制 10 | warnings.filterwarnings("ignore") 11 | 12 | # テスト用データパスを定義 13 | DATA_PATH = os.path.join(os.path.dirname(__file__), "../data/Titanic.csv") 14 | 15 | 16 | @pytest.fixture 17 | def sample_data(): 18 | """Titanicテスト用データセットを読み込む""" 19 | return pd.read_csv(DATA_PATH) 20 | 21 | 22 | def test_data_exists(sample_data): 23 | """データが存在することを確認""" 24 | assert not sample_data.empty, "データセットが空です" 25 | assert len(sample_data) > 0, "データセットにレコードがありません" 26 | 27 | 28 | def test_data_columns(sample_data): 29 | """必要なカラムが存在することを確認""" 30 | expected_columns = [ 31 | "Pclass", 32 | "Sex", 33 | "Age", 34 | "SibSp", 35 | "Parch", 36 | "Fare", 37 | "Embarked", 38 | "Survived", 39 | ] 40 | for col in expected_columns: 41 | assert ( 42 | col in sample_data.columns 43 | ), f"カラム '{col}' がデータセットに存在しません" 44 | 45 | 46 | def test_data_types(sample_data): 47 | """データ型の検証""" 48 | # 数値型カラム 49 | numeric_columns = ["Pclass", "Age", "SibSp", "Parch", "Fare"] 50 | for col in numeric_columns: 51 | assert pd.api.types.is_numeric_dtype( 52 | sample_data[col].dropna() 53 | ), f"カラム '{col}' が数値型ではありません" 54 | 55 | # カテゴリカルカラム 56 | categorical_columns = ["Sex", "Embarked"] 57 | for col in categorical_columns: 58 | assert ( 59 | sample_data[col].dtype == "object" 60 | ), f"カラム '{col}' がカテゴリカル型ではありません" 61 | 62 | # 目的変数 63 | survived_vals = sample_data["Survived"].dropna().unique() 64 | assert set(survived_vals).issubset({"0", "1"}) or set(survived_vals).issubset( 65 | {0, 1} 66 | ), "Survivedカラムには0, 1のみ含まれるべきです" 67 | 68 | 69 | def test_missing_values_acceptable(sample_data): 70 | """欠損値の許容範囲を確認""" 71 | # 完全に欠損するのではなく、許容範囲内の欠損を確認 72 | for col in sample_data.columns: 73 | missing_rate = sample_data[col].isna().mean() 74 | assert ( 75 | missing_rate < 0.8 76 | ), f"カラム '{col}' の欠損率が80%を超えています: {missing_rate:.2%}" 77 | 78 | 79 | def test_value_ranges(sample_data): 80 | """値の範囲を検証""" 81 | context = gx.get_context() 82 | data_source = context.data_sources.add_pandas("pandas") 83 | data_asset = data_source.add_dataframe_asset(name="pd dataframe asset") 84 | 85 | batch_definition = data_asset.add_batch_definition_whole_dataframe( 86 | "batch definition" 87 | ) 88 | batch = batch_definition.get_batch(batch_parameters={"dataframe": sample_data}) 89 | 90 | results = [] 91 | 92 | # 必須カラムの存在確認 93 | required_columns = [ 94 | "Pclass", 95 | "Sex", 96 | "Age", 97 | "SibSp", 98 | "Parch", 99 | "Fare", 100 | "Embarked", 101 | ] 102 | missing_columns = [ 103 | col for col in required_columns if col not in sample_data.columns 104 | ] 105 | if missing_columns: 106 | print(f"警告: 以下のカラムがありません: {missing_columns}") 107 | return False, [{"success": False, "missing_columns": missing_columns}] 108 | 109 | expectations = [ 110 | gx.expectations.ExpectColumnDistinctValuesToBeInSet( 111 | column="Pclass", value_set=[1, 2, 3] 112 | ), 113 | gx.expectations.ExpectColumnDistinctValuesToBeInSet( 114 | column="Sex", value_set=["male", "female"] 115 | ), 116 | gx.expectations.ExpectColumnValuesToBeBetween( 117 | column="Age", min_value=0, max_value=100 118 | ), 119 | gx.expectations.ExpectColumnValuesToBeBetween( 120 | column="Fare", min_value=0, max_value=600 121 | ), 122 | gx.expectations.ExpectColumnDistinctValuesToBeInSet( 123 | column="Embarked", value_set=["C", "Q", "S", ""] 124 | ), 125 | ] 126 | 127 | for expectation in expectations: 128 | result = batch.validate(expectation) 129 | results.append(result) 130 | is_successful = all(result.success for result in results) 131 | assert is_successful, "データの値範囲が期待通りではありません" 132 | -------------------------------------------------------------------------------- /day5/演習3/tests/test_model.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pytest 3 | import pandas as pd 4 | import numpy as np 5 | import pickle 6 | import time 7 | from sklearn.ensemble import RandomForestClassifier 8 | from sklearn.model_selection import train_test_split 9 | from sklearn.metrics import accuracy_score 10 | from sklearn.impute import SimpleImputer 11 | from sklearn.preprocessing import OneHotEncoder, StandardScaler 12 | from sklearn.compose import ColumnTransformer 13 | from sklearn.pipeline import Pipeline 14 | 15 | # テスト用データとモデルパスを定義 16 | DATA_PATH = os.path.join(os.path.dirname(__file__), "../data/Titanic.csv") 17 | MODEL_DIR = os.path.join(os.path.dirname(__file__), "../models") 18 | MODEL_PATH = os.path.join(MODEL_DIR, "titanic_model.pkl") 19 | 20 | 21 | @pytest.fixture 22 | def sample_data(): 23 | """テスト用データセットを読み込む""" 24 | if not os.path.exists(DATA_PATH): 25 | from sklearn.datasets import fetch_openml 26 | 27 | titanic = fetch_openml("titanic", version=1, as_frame=True) 28 | df = titanic.data 29 | df["Survived"] = titanic.target 30 | 31 | # 必要なカラムのみ選択 32 | df = df[ 33 | ["Pclass", "Sex", "Age", "SibSp", "Parch", "Fare", "Embarked", "Survived"] 34 | ] 35 | 36 | os.makedirs(os.path.dirname(DATA_PATH), exist_ok=True) 37 | df.to_csv(DATA_PATH, index=False) 38 | 39 | return pd.read_csv(DATA_PATH) 40 | 41 | 42 | @pytest.fixture 43 | def preprocessor(): 44 | """前処理パイプラインを定義""" 45 | # 数値カラムと文字列カラムを定義 46 | numeric_features = ["Age", "Pclass", "SibSp", "Parch", "Fare"] 47 | categorical_features = ["Sex", "Embarked"] 48 | 49 | # 数値特徴量の前処理(欠損値補完と標準化) 50 | numeric_transformer = Pipeline( 51 | steps=[ 52 | ("imputer", SimpleImputer(strategy="median")), 53 | ("scaler", StandardScaler()), 54 | ] 55 | ) 56 | 57 | # カテゴリカル特徴量の前処理(欠損値補完とOne-hotエンコーディング) 58 | categorical_transformer = Pipeline( 59 | steps=[ 60 | ("imputer", SimpleImputer(strategy="most_frequent")), 61 | ("onehot", OneHotEncoder(handle_unknown="ignore")), 62 | ] 63 | ) 64 | 65 | # 前処理をまとめる 66 | preprocessor = ColumnTransformer( 67 | transformers=[ 68 | ("num", numeric_transformer, numeric_features), 69 | ("cat", categorical_transformer, categorical_features), 70 | ] 71 | ) 72 | 73 | return preprocessor 74 | 75 | 76 | @pytest.fixture 77 | def train_model(sample_data, preprocessor): 78 | """モデルの学習とテストデータの準備""" 79 | # データの分割とラベル変換 80 | X = sample_data.drop("Survived", axis=1) 81 | y = sample_data["Survived"].astype(int) 82 | X_train, X_test, y_train, y_test = train_test_split( 83 | X, y, test_size=0.2, random_state=42 84 | ) 85 | 86 | # モデルパイプラインの作成 87 | model = Pipeline( 88 | steps=[ 89 | ("preprocessor", preprocessor), 90 | ("classifier", RandomForestClassifier(n_estimators=100, random_state=42)), 91 | ] 92 | ) 93 | 94 | # モデルの学習 95 | model.fit(X_train, y_train) 96 | 97 | # モデルの保存 98 | os.makedirs(MODEL_DIR, exist_ok=True) 99 | with open(MODEL_PATH, "wb") as f: 100 | pickle.dump(model, f) 101 | 102 | return model, X_test, y_test 103 | 104 | 105 | def test_model_exists(): 106 | """モデルファイルが存在するか確認""" 107 | if not os.path.exists(MODEL_PATH): 108 | pytest.skip("モデルファイルが存在しないためスキップします") 109 | assert os.path.exists(MODEL_PATH), "モデルファイルが存在しません" 110 | 111 | 112 | def test_model_accuracy(train_model): 113 | """モデルの精度を検証""" 114 | model, X_test, y_test = train_model 115 | 116 | # 予測と精度計算 117 | y_pred = model.predict(X_test) 118 | accuracy = accuracy_score(y_test, y_pred) 119 | 120 | # Titanicデータセットでは0.75以上の精度が一般的に良いとされる 121 | assert accuracy >= 0.75, f"モデルの精度が低すぎます: {accuracy}" 122 | 123 | 124 | def test_model_inference_time(train_model): 125 | """モデルの推論時間を検証""" 126 | model, X_test, _ = train_model 127 | 128 | # 推論時間の計測 129 | start_time = time.time() 130 | model.predict(X_test) 131 | end_time = time.time() 132 | 133 | inference_time = end_time - start_time 134 | 135 | # 推論時間が1秒未満であることを確認 136 | assert inference_time < 1.0, f"推論時間が長すぎます: {inference_time}秒" 137 | 138 | 139 | def test_model_reproducibility(sample_data, preprocessor): 140 | """モデルの再現性を検証""" 141 | # データの分割 142 | X = sample_data.drop("Survived", axis=1) 143 | y = sample_data["Survived"].astype(int) 144 | X_train, X_test, y_train, y_test = train_test_split( 145 | X, y, test_size=0.2, random_state=42 146 | ) 147 | 148 | # 同じパラメータで2つのモデルを作成 149 | model1 = Pipeline( 150 | steps=[ 151 | ("preprocessor", preprocessor), 152 | ("classifier", RandomForestClassifier(n_estimators=100, random_state=42)), 153 | ] 154 | ) 155 | 156 | model2 = Pipeline( 157 | steps=[ 158 | ("preprocessor", preprocessor), 159 | ("classifier", RandomForestClassifier(n_estimators=100, random_state=42)), 160 | ] 161 | ) 162 | 163 | # 学習 164 | model1.fit(X_train, y_train) 165 | model2.fit(X_train, y_train) 166 | 167 | # 同じ予測結果になることを確認 168 | predictions1 = model1.predict(X_test) 169 | predictions2 = model2.predict(X_test) 170 | 171 | assert np.array_equal( 172 | predictions1, predictions2 173 | ), "モデルの予測結果に再現性がありません" 174 | --------------------------------------------------------------------------------