KBproject_備忘録_0

### このプロジェクトのGithubは非公開としよう。
## 2024-10-15
- CustomGPT2Tokenizerを使っている場合の、前回のtokenizerファイルのロードを試行錯誤中
- トークナイザーのロードに必要な引数
    - vocab_file: 語彙ファイル（vocab.json）
    - merges_file: マージファイル（merges.txt）
    - bos_token, eos_token, pad_token, unk_token: 特殊トークン（BOS, EOS, PAD, UNK）
- 作成時と同じように、GPT2Tokenizerをオーバーライドする必要がある
    - 前回
        ```
        # カスタムGPT2トークナイザーの定義
        class CustomGPT2Tokenizer(GPT2Tokenizer):
            def __init__(
                    self, 
                    vocab_file, 
                    merges_file, 
                    bos_token="<S>", 
                    eos_token="<E>", 
                    pad_token="<PAD>", 
                    unk_token="<UNK>", 
                    *args, 
                    **kwargs
                ):
                
                # 親クラスに必要な引数とオプションのトークンを渡す
                super().__init__(
                            vocab_file=vocab_file, 
                            merges_file=merges_file, 
                            bos_token=bos_token, 
                            eos_token=eos_token, 
                            pad_token=pad_token, 
                            unk_token=unk_token, 
                            *args, 
                            **kwargs
                        )
                
                self.bos_token = bos_token
                self.eos_token = eos_token
                self.unk_token = unk_token
                self.pad_token = pad_token
            
                # vocab.jsonを読み込んで、self.vocabにセット
                with open(vocab_file, 'r', encoding='utf-8') as f:
                    self.vocab = json.load(f)

        ```
        として作成したのであれば、同じようにこの CustomGPT2Tokenizer クラスを用いて
        ```
        # CustomGPT2Tokenizerを使ってロードする
        tokenizer_config = self.gpt2_config['tokenizer_config']  # 設定からトークナイザーのファイルパスを取得
        
        custom_tokenizer = self.CustomGPT2Tokenizer(
            vocab_file=tokenizer_config['vocab_file'],
            merges_file=tokenizer_config['merges_file'],
            bos_token=self.special_tokens["bos_token"],
            eos_token=self.special_tokens["eos_token"],
            pad_token=self.special_tokens["pad_token"],
            unk_token=self.special_tokens["unk_token"]
        )
        
        print(f"トークナイザーが正常にロードされました。語彙サイズ: {len(custom_tokenizer)}")

        ```
        ロードするべきですよね。サボったらエラーが出ますよね。ですよね。

- 次やること
    ![Screenshot from 2024-10-15 20-02-09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3910368/7b25972d-00ef-7ae8-c006-3f978f82864d.png)

## 2024-10-16
- 続き。CustomGPT2Tokenizerをつかおう。いつもどおり作るだけ。
    ```
    class CustomGPT2Tokenizer(GPT2Tokenizer):            
        def __init__(
                self, 
                vocab_file, 
                merges_file, 
                bos_token="<S>", 
                eos_token="<E>", 
                pad_token="<PAD>", 
                unk_token="<UNK>", 
                *args, 
                **kwargs
            ):
            
            # 親クラスに必要な引数とオプションのトークンを渡す
            super().__init__(
                        vocab_file=vocab_file, 
                        merges_file=merges_file, 
                        bos_token=bos_token, 
                        eos_token=eos_token, 
                        pad_token=pad_token, 
                        unk_token=unk_token, 
                        *args, 
                        **kwargs
                    )
    ```
    自分は辞書のパスと、マージーズパスを指定すればいいだけね。
    　
    わ、まだpythonわからないことたくさんね。
    *args と **kwargsの特性ついては次の通りみたい。
    ```
    def sample_func(*args):
        for arg in args:
            print(arg)
    
        sample_func(1, 2, 3)
        
        # 出力:
        # 1
        # 2
        # 3
    ```
    ```
    def sample_func(**kwargs):
        for key, value in kwargs.items():
            print(f"{key}: {value}")
    
        sample_func(name="まなみ", age=25)
        
        # 出力:
        # name: まなみ
        # age: 25
    ```
    今回のように大型のクラスをオーバーライドしているときに、おお助かりですね。
    　
    うわぁ…もう出勤する時間だ
    ```
                
            # CustomGPT2Tokenizerで作ったtokenizerなんだから、CuxtomGPT2Tolenizerでロードしなきゃです。
            vocab_file_path = f"{current_dir}/gpt2_tokenizer/vocab.json"
            merges_file_path = f"{current_dir}/merges.txt"
            custom_tokenizer = self.one_time_world_instance.setupGpt2Player.CustomGPT2Tokenizer(
                vocab_file=tokenizer_config['vocab_file'],
                merges_file=tokenizer_config['merges_file'],
                bos_token=self.special_tokens["bos_token"],
                eos_token=self.special_tokens["eos_token"],
                pad_token=self.special_tokens["pad_token"],
                unk_token=self.special_tokens["unk_token"]
            )
            
    ```

## 2024-10-18
- ２日ぶりの作業なのでなにをしていたか忘れた。思い出そう。
- ああ、tokenizerとmergesをロードするんだった。
    ```
    # CustomGPT2Tokenizerで作ったtokenizerなんだから、CuxtomGPT2Tolenizerでロードしなきゃです。
    vocab_file_path = f"{current_dir}/gpt2_tokenizer/vocab.json"
    merges_file_path = f"{current_dir}/merges.txt"
    
    # カスタムトークナイザの呼び出し、インスタンスの作成
    custom_tokenizer = self.one_time_world_instance.setupGpt2Player.CustomGPT2Tokenizer(
        vocab_file=tokenizer_config['vocab_file'],
        merges_file=tokenizer_config['merges_file'],
        bos_token=self.special_tokens["bos_token"],
        eos_token=self.special_tokens["eos_token"],
        pad_token=self.special_tokens["pad_token"],
        unk_token=self.special_tokens["unk_token"]
    )
    
    ```

    特殊トークン、辞書は
    ```
    gpt2_tokenizer/
    ├── merges.txt
    ├── special_tokens_map.json ★
    ├── tokenizer_config.json
    └── vocab.json ★
    ```
    ココから取らなきゃ。

    もう仕事…さよなら


## 2024-10-20
- tokenizerとmergesをロード
    ```
    # CustomGPT2Tokenizerで作ったtokenizerなんだから、CuxtomGPT2Tolenizerでロードしなきゃです。
    vocab_file_path = f"{current_dir}/gpt2_tokenizer/vocab.json"
    merges_file_path = f"{current_dir}/gpt2_tokenizer/merges.txt"
    
    # カスタムトークナイザの呼び出し、インスタンスの作成
    custom_tokenizer = self.one_time_world_instance.setupGpt2Player.CustomGPT2Tokenizer(
        vocab_file=vocab_file_path,
        merges_file=merges_file_path,
    )    
    ```
- 「model.transformer.wte.weight.shape」が(50257, 768)だった場合、これは50257個の単語（トークン）がるということ。

## 2024-10-22
- なにしてたんだっけ、、
    ああ。
    新しい語彙と、その語彙の単語表現ベクトルを作成するところまでできたんだ。
    この語彙とベクトルの辞書を、modelに反映させよう。

- 新しい語彙とベクトルの辞書をmodelに反映する
    https://chatgpt.com/share/671782f9-4890-8012-a4e6-7a9987b867f1　（めｍ）
    
    
## 2024-10-23
- 可読性が低いから変数名を修正する
    ```
    """ 前回（存在する場合） """
    previous_model_path = f"{current_dir}/gpt2_model.bin"
    previous_optimizer_path = f"{current_dir}/optimizer.pt"
    previous_tokenizer_path = f"{current_dir}/gpt2_tokenizer"

    result_dict = self.load_and_update(
        tokenizer_path=tokenizer_path, 
        optimizer_path=optimizer_path, 
        model_path=model_path, 
        custom_tokenizer=self.custom_tokenizer, 
        model=self.model, 
        optimizer=optimizer,
    )
    ```
## 2024-10-25
- 変数名を明瞭的に変更
- 前回のモデルのロード

## 2024-10-26
- なんかmodelの保存時、重みのみの保存としていたから、model全てを保存するように修正した
- バックアップとろう。　

## 2024-10-28
- んあそうだバックアップ取るんだったもうにかげつとってない笑、わらえない。
- 続き。だけどなんかコードが頭に入ってこない。
    あー、理解した。
    うちさ、なんかpythonの戻り値を辞書で返せって、ほとんどのサイトで言ってるように感じるんだけど、著しく可読性が落ちると思うんだよね。
    こんかい、辞書にしてみたんだけど、実装してから月日が経ってから観ると、たしかに読みにくいんだよ
    
    ```
    
                """  """
                result_dict = self.load_and_update(
                    previous_tokenizer_path=previous_tokenizer_path, 
                    previous_optimizer_path=previous_optimizer_path, 
                    previous_model_path=previous_model_path, 
                    this_time_custom_tokenizer=self.this_time_custom_tokenizer, 
                    this_time_model=self.this_time_model, 
                    this_time_optimizer=this_time_optimizer,
                )
                
                self.model = result_dict["model"]
                optimizer = result_dict["optimizer"]
                self.custom_tokenizer = result_dict["tokenizer"]
    
    
    ```
    
    わかりやすいように、前回の私はすぐ変数を作成しているけれど。
    これは、ほんとにだめな実装ね。
    
    もちろん、大量に戻り地があったらどうするの？っていう他の人からの質問が飛び交うと思うのだけれど、そもそも大量に戻値がある時点で、そのメソッドはだめなメソッドと感じるよ。デザインパターンでそれは更に単純化できるはずだ。特に私のデザインパターンはそれが可能である。単純に大量のクラスで処理を小分けすれば良いのです。
    独学野生プログラマーなので、もし天才様がわたしのこんな小さい記事に飛んできた場合はごめんなさい。

    なにを言ってるんだ
- 実装を進めていく。
    実装の流は次の通り
    ```
    """ 流れ
    1. {今回増えた語彙:その語彙のランダムベクトル}を作成
       - 新たに追加されたトークンに対して、埋め込みベクトルをランダムに初期化し、辞書形式で`missing_token_weights`に保存する。
       - 使用する変数:
         - `missing_token_weights`: 新しいトークンとそのランダムベクトルを格納する辞書。
         - `embedding_dim`: 埋め込みベクトルの次元数（モデルの設定から取得）。
         - `missing_tokens`: 新たに追加されたトークンのリスト。
    
    2. 前回の重みに今回増えた語彙を追加登録
       - トークナイザーに新しいトークンを追加し、埋め込み層のサイズを調整する。
       - モデルの`EmbeddingLayer`に、新たなトークンの重みベクトルを設定することで、モデルが新しい語彙を認識できるようにする。
       - 使用する変数:
         - `self.this_time_custom_tokenizer`: 現在使用しているトークナイザー。新しいトークンを追加する。
         - `this_time_model`: 現在のモデル。埋め込み層をリサイズし、重みを設定する。
    
    3. トークナイザーと埋め込み層の更新
       - トークナイザーに新しいトークンを追加後、`this_time_model`の埋め込み層のサイズを更新。
       - 追加されたトークンの初期化済みベクトルを`EmbeddingLayer`に反映。
       - 使用する変数:
         - `self.this_time_custom_tokenizer`: トークナイザー。
         - `this_time_model`: モデルの埋め込み層。
    
    4. オプティマイザの状態を更新（存在する場合）
       - 前回のオプティマイザの状態をロードし、学習を再開するための準備をする。
       - 使用する変数:
         - `this_time_optimizer`: 現在使用しているオプティマイザ。
         - `previous_optimizer_path`: 前回のオプティマイザの状態を保存したファイルパス。
    
    5. モデルの設定調整（追加学習に必要な調整）
       - モデルの学習時の設定やパラメータの調整を行い、新しいデータで学習できる状態にする。
       - 使用する変数:
         - `this_time_model`: 現在のモデル。
         - `this_time_optimizer`: オプティマイザ。
    """

    ```

## 2024-11-05
- apiがおかしい。一旦無料のapiで実験して、大丈夫であれば、、ここ、アカウント制じゃないからよくわからないんだよね、イライラする、、
    - レスポンスが可笑しい。全て空の辞書が返される。urlチェッカーを用意した。
        ```
        def check_api_key_status(self, test_url=None):
            """
            APIキーの有効性を確認し、アクセス制限やエラーが発生していないかをチェックします。
            """
            url = test_url if test_url else self.default_test_url
            response = requests.get(url)
            data = response.json()
    
            # 結果メッセージを作成
            if not data:  # dataが空のとき
                status = "データが取得できませんでした。アクセス制限または不明なエラーが発生している可能性があります。"
            elif "Error Message" in data:
                status = "APIキーが無効か、アクセス制限がかかっています。"
            elif "Note" in data:
                status = "APIリクエストの制限に達した可能性があります。"
            else:
                status = "APIキーに問題はありませんでした。"
            
            # dataの最初の8行を表示
            print("取得したデータの最初の8行:")
            for key, value in itertools.islice(data.items(), 8):
                print(f"{key}: {value}")
    
            return status        
        ```
        次のように取得後にすぐ実行するように組み込んだ。
        ```
        # apiの取得
        aLPHAVANTAGE_API = ALPHAVANTAGE_API()
        api_key = aLPHAVANTAGE_API.API_OPEN_KEY # アルファバンテージ

        url = f'https://www.alphavantage.co/query?function=LISTING_STATUS&apikey={api_key}'
        
        # apikeyのチェック (apikeyが有効であれば、有効とprintされます。)
        print(self.check_api_key_status(test_url=url))
        ```

    - サーバーがダウンしている模様。全然日本人alphavantageを使っていないのでニュースがマジでない。Twitterで呟いたところ、海外の方が奇跡的に反応してくれた。
    ![IMG_0001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3910368/6fc8b69f-c2e7-af1f-73f8-4c6dcab742a1.png)

    この方は、「まだダウンしています、早く復帰することを願います」とお返事を下さったようです。
    ホント、この方と繋がれてよかった…自分のAPIキーに問題なのか、コードに問題なのか、ものすごく考えてしまった。よかった…死ぬかと思った…

        
## 2024-11-06
- 足りない実装(追加学習に置いて)
```
1. 新しい埋め込みベクトルの学習許可
新しい埋め込みベクトルが学習可能な状態であることを確認するには、モデルの埋め込み層をリサイズした後、全てのパラメータがrequires_grad=Trueになっているか確認します。
追加の操作として、以下のコードを使って埋め込み層全体が学習対象かを確認してみましょう。
python
コードをコピーする
# 埋め込み層のパラメータを学習対象に設定
model.transformer.wte.weight.requires_grad = True

# 確認用：埋め込み層の学習状態をチェック
print("埋め込み層の requires_grad 状態:", model.transformer.wte.weight.requires_grad)
これで新しい語彙の埋め込みベクトルも、追加学習中に学習されるはずよ。

2. 追加学習の損失計算設定
損失計算にはCrossEntropyLossを使用するのが一般的ね。新しい語彙が含まれても、モデルの最終出力層がトークナイザーに対応するようにリサイズ済みであれば、そのまま損失関数が適用できるわ。
python
コードをコピーする
from torch.nn import CrossEntropyLoss

# 損失関数の設定
loss_fn = CrossEntropyLoss()

# 追加学習時の損失計算例
outputs = model(input_ids)  # モデルからの出力
logits = outputs.logits  # ロジット（生の出力）

# ラベル（正解データ）とロジットで損失計算
loss = loss_fn(logits.view(-1, logits.size(-1)), labels.view(-1))
追加語彙を含めたモデル全体で損失が計算されるはずよ。これで学習時の更新が期待通りに行えるわ。

3. 学習データの再準備
トークナイザーが新しい語彙を含むデータに対応できるよう、tokenizeメソッドが正しく動作するかチェックしてね。例えば、以下のコードで新しい語彙が適切にトークン化されているか確認するのが良いわ。
python
コードをコピーする
# 新しい語彙を含む文章をトークナイズ
sample_text = "ここに新しい語彙を含むテキストを入れてみて"
tokens = this_time_custom_tokenizer.tokenize(sample_text)
print("トークナイズ結果:", tokens)
もし新しい語彙がトークナイザーに認識されていない場合、UNK（未登録トークン）で出力されることがあるわ。そうでなければ、新しい語彙が正しくトークン化できているから安心してね。

4. 追加語彙とモデルの相互作用の確認
モデルの出力層が、トークナイザーのサイズに合わせてリサイズされているか確認しておくことが大事よ。出力層がトークナイザーと一致していないと、損失計算の際にエラーが発生する可能性があるわ。
python
コードをコピーする
# トークナイザーとモデルの埋め込みサイズの一致を確認
tokenizer_vocab_size = len(this_time_custom_tokenizer)
model_vocab_size = model.transformer.wte.weight.size(0)

if tokenizer_vocab_size != model_vocab_size:
    print("警告: トークナイザーとモデルの語彙サイズが一致していません！")
else:
    print("トークナイザーとモデルの語彙サイズが一致しています。")
これで、トークナイザーのサイズに合わせて埋め込み層と出力層も調整されているか確認できるわ。

5. 学習スケジューラの設定
学習スケジューラは追加学習で特に重要よ。追加学習用に学習率を少し低めに設定し、スケジューラで徐々に減少させるのがおすすめね。
python
コードをコピーする
from torch.optim.lr_scheduler import StepLR

# スケジューラの設定
scheduler = StepLR(optimizer, step_size=10, gamma=0.1)  # 10エポックごとに学習率を0.1倍に減少

# 例: 学習ループ内でスケジューラをステップ更新
for epoch in range(num_epochs):
    optimizer.zero_grad()
    outputs = model(input_ids)
    loss = loss_fn(outputs.logits.view(-1, outputs.logits.size(-1)), labels.view(-1))
    loss.backward()
    optimizer.step()
    scheduler.step()  # スケジューラの更新
スケジューラで学習率を制御することで、追加学習の安定性が高まるわよ。
```

## 2024-11-07
- 新しい語彙をさ、
```
            model.resize_token_embeddings(len(previous_custom_tokenizer))

```
これでEmbeddingLayerの単語表現ベクトルに追加してるんだけどさ。
このとき、重みもランダムで自動初期化してくれてるんだってさ。
えー、
```
            # EmbeddingLayerの重みの、１単語あたりのベクトルのLength数を取得
            missing_token_weights = {}
            embedding_dim = this_time_model.transformer.wte.weight.shape[1]  # 埋め込み次元（重みのベクトルサイズ）

            # 新しい語彙の重みを用意。{新しい語彙:新しいベクトル}とする辞書を作成
            for token in missing_tokens:
                # ランダムに初期化した重みベクトルを作成
                random_weight = torch.randn(embedding_dim).tolist()
                missing_token_weights[token] = random_weight

            # 新しい語彙と、単語表現ベクトルの辞書の完成。
            RESET = '\033[0m'
            BRIGHT_GREEN = '\033[92m'
            print(f"\n{BRIGHT_GREEN}新しい語彙と、その単語ベクトルのペアを確認のためDataFrameで出力します。")
            df = pd.DataFrame(list(missing_token_weights.items()), columns=["新しい語彙", "新しい単語ベクトル表現"]) # 辞書をDataFrameに変換
            print(f"{df}{RESET}") # 表示



            """ 前回のトークナイザーに新しい語彙（トークン）を追加 """
            previous_custom_tokenizer.add_tokens(list(missing_token_weights.keys()))

            """  """
            # EmbeddingLayerの単語表現に、あたらしい語彙分を追加（実はこのときランダムなベクトルも自動追加される。）
            model.resize_token_embeddings(len(previous_custom_tokenizer))

            # 新しく追加された語彙に対応するベクトルをモデルに反映する
            with torch.no_grad():  # 勾配計算を無効にする（本意味では意味ないが、やるのが安全らしい…これに関してはtorchライブラリ観なきゃわからん）
                for token, weight in missing_token_weights.items():  # 辞書の中の各単語とそのベクトルを順に取得
                    token_id = self.this_time_custom_tokenizer.convert_tokens_to_ids(token)  # トークンを対応するIDに変換
                    # モデルの埋め込み層に、変換したIDの位置に埋め込みベクトルを代入
                    model.transformer.wte.weight[token_id] = torch.tensor(weight)

```
わざわざ別でランダムに初期化したものを別で用意して反映させるやりかたでやってたよ、、、
って。
でも直さないでおこう。処理は遅くなるけれど、応用できるコードになりました。



## 2024-11-14
最近ねれない…あと３０分もない、、出勤になる…ご飯食べなきゃ、、

- 追加学習のコーディングの続き。
    - 追加学習２回目以降で、何故か検証データのlossが等しくなってしまう現象が発生
    単語IDの正確性に問題でしょうか。一旦検証データ、学習データともに推論結果を出力して確認しよう。

## 2024-11-15
このデバッグ難しいぞ

- 検証データの方だけlossが一定になる。
確認することはいまとっさに思いつくとしたら、、
・IDの一致不一致
・データの損傷や文字化けの確認
・過学習や爆発が起きてないか確認

## 2024-11-19
- デバッグについて。
    - 検証データのlossのテスト算出が失敗(…全て同じになる)する現象は、2回目(つまり追加学習)以降から発生する。
    したがって、データを追加するコードを間違えている可能性が高いと見よう。

    - まずは、、、検証データの推論結果のlossがおかしいのであれば、
    検証データを推論している箇所をデバッグするのが、普通ですよね。






## 2024-11-19
- つらい、、、
たくさんデバッグをしているが、大変だ。。。。
ひとまず、早く動かしたい。なので。
- "学習できていればよし" と判断しよう。

## 2024-11-20
- Awawaさんがjoinしてくれて、ちょっと元気になった。嬉しかった。
- 学習できていればよし、とするということで、、ちょっとまとめる。
    - もちろん学習ができていることを確認したとしても、検証データのlossが等しくなる原因を突き止める。
    - 今回"学習できていればよし"とする決断をするためのデバッグへ移行する理由は、
        - 学習完了後にデバッグしてみた。方法は、バッチごとにロジットを確認する形でデバッグをした。
        現在
            - ３エポック
            - 1エポックあたり４バッチ
        での学習をしている。
        
        １エポックの学習完了後に、
        デバッグとして
        訓練データ、検証データを４つ取得し、それぞれmodelでロジットを確認するデバッグコードを噛ませたところ、
        訓練データはDataLoaderで
        shuffle=True
        としているのでもちろん取得される４つのデータはエポックごとに異なるため、lossはほぼ確実に変化する。
        しかし、検証データの方はシャッフルさせていないため、1エポック終了後毎度同じ４つのデータが取得される。さらに、検証データは４つしか用意していないため、必ず１エポック終了後、同じ検証データを確認していることとなる。
        今回のデバッグは、
        １エポックごとに訓練データ４つ、検証データ４つの合計８つのデータをデバッグでロジットを確認する形を取った
        ということです。
        このデバッグの結果は次の通り。
        
        １エポック終了時点に置いて、
        - 訓練データの４バッチのlossはエポックごとに異なる。（シャッフルされているので、数百ファイルから４つサンプルを取得して確認するので、ほぼ当たり前だ。）
        - 検証データの４バッチのlossはエポックごとに"同じである"。
        さて、今回ロジットを確認させたわけなので、データも見れるわけだ。
        訓練データ、検証データともに、どのようなデータでロジットを確認しているのかを目視してみた。
        それでわかったことが次の通り。
        - 訓練データはエポック１回め、２回め、３回目、４回目、確認サンプルは重複していない。
        - 検証データは、全てのエポックに置いて、同じサンプルでロジットを確認している。
        
        これは、復唱するようだが、訓練データ群は、エポック毎回データをシャッフルしているからである。検証データ群はシャッフルしていないからである（というかそもそも検証データのサンプル数は４つしか用意していない）。
        この２つの情報から理解することはできる。
        しかし、デバッグで気がついたことは他にもある。
        検証データの方に問題があったのだ。
        いや、もしかしたら訓練データにも問題があるかも知れない。
        その問題の内容とは、検証データでデバッグとして確認した４つのデータについてだ。
        なんと、検証データ側では、４エポック毎回、同じlossを表示しているのだ。
        …そもそも今回のデバッグの目的は「検証データのみ、なぜかエポック毎回のlossが等しくなってしまう…デバッグしよう」であるわけだが、
        今回はmodelに置いて最終層の出力のロジットもエポック毎に表示させたことにミソがある。
        １エポック終了後、２エポック目には
        - １エポック終了し、パラメータを更新した重みやバイアス等
        を、引き継がないで２エポック目に入り、学習していると仮定することができる。
        - 今回のデバッグで得られた結果
            - エポック毎回同じ学習をしているのかも知れない。もしくは、エポック毎回異なるデータを学習しているが、誤って１回目のエポックのロジットを４連続表示させているデバッグミスをしている。
        
            ということにおそらくなるだろう。
    ```
    上記のことから、エポック数を1として設定して学習すれば、現在のmodel設定でも
    パラメータ的には学習できている可能性があるため、一度ある程度学習させてから
    いくつか普通に推論させてみて、どれほど正しく推論されるかを確認してみよう。
    正しい推論結果に近づいているのであれば、学習できているとしたい。
    ```

    
## 2024-11-21
ちょっと諦めきれないので、もうすこしlossについてデバッグする。
前回のデバッグより、
- １エポック終了後に、modelの更新が行われていない
だけかも知れないので、そこだけ確認したい。
だめだ、しっかり更新しているようにしか見えない

単純に学習できているかどうかを、推論による正答率を算出する形で確かめよう。
あえて過学習をさせることもしてみよう。


- GPT2PredictPlayerを作成する
    - GPT2TrainPlayerのメソッドを呼び出す形で組み込もう

## 2024-11-24
だるい、学習データに\nが含まれていることに気がついた。
今回は絶対改行は含まないので、学習前に取り除く処理をほどこす。
TextDatasetを改良
```


    class TextDataset(Dataset): # Datasetをスーパークラスとして引き継ぎ。
        """
        テキストファイルを読み込み、トークナイズしてデータセットを作成するクラス
        """
        def __init__(self, file_path, tokenizer, max_length=None):
            self.examples = []

            with open(file_path, 'r', encoding='utf-8') as f:
                
                lines = [line.replace('\n', '') for line in f.readlines()]  
                
                for line in lines:
                    
                    tokenized_input = tokenizer(line, return_tensors='pt', max_length=max_length, truncation=False, padding='max_length') # truncation=False 万が一max_lengthを超えた場合、切り詰めないようにする。今回データは全て同じ長さのはずだから、あえてFalseにして、エラーを出させよう。
                    input_ids = tokenized_input['input_ids'].squeeze(0)
                    
                    labels = input_ids.clone()  # 入力とラベルは同じ（自動的に１トークンずらすようにしてラベル化してくれる。）

                    self.examples.append({'input_ids': input_ids, 'labels': labels})                    

                

```
あとで学習プレイヤーでも、なにを学習させているのかをしっかり確認できるようにしとかなきゃな、、、

うん、同じですね。
```


            # モデルを学習モードに設定
            self.model.train()
            
            # 学習ループの設定
            epochs = epoch_size
            
            if epoch_size == 0:
                print("エポックに0が設定されているため、学習をスキップします。")
                return
            
            for epoch in range(epochs):
                print(f"\n=== Epoch {epoch + 1}/{epochs} ===")
                total_loss = 0


                # ***進捗バーの設定***
                progress_bar = tqdm(trainLoader, desc=f"Training Epoch {epoch + 1}/{epochs}")  # ***進捗バーを表示***

                # 学習ステップ
                for batch_index, batch in enumerate(progress_bar):  # ***進捗バーでラップ***

                    try:
                        # 各バッチからデータを取り出す
                        inputs = batch['input_ids']  # バッチ内の入力データ
                        labels = batch['labels']  # バッチ内の正解データ
                        
                        print(inputs)
                        print()
                        print(labels)

                        # ***デバイスにデータを移動***
                        inputs = inputs.to(self.device)
                        labels = labels.to(self.device)

                    except RuntimeError as e:
                        print(f"データをGPUに移動中にエラーが発生しました: {e}")
                        raise e  # エラーを再度発生させる

                    exit()

```
学習の方も問題なさそう。torchでは、自動で勝手にlabelが１ポジジョンずれてくれるからね。


## 2024-11-25（っていうかしばらく日付角の忘れてた）
追加学習以降の、学習がおかしい。

新しい語彙を、予め全て追加しておいて、語彙が追加されない状態で、追加学習をしてみる。

これでおかしくならなければ、新しい語彙の追加によってなにか変になっているはずだ。

おかしくなれば、語彙の追加は関係ないということだ。

### class DownloadTrainingDataPlayer(SuperPlayer):
に、
add_stock_symbols_to_vocabを追加した。
このメソッドを、
SetupGpt2Playerの    
def create_or_update_vocab(self, txt_paths=[]):
メソッドに
```
        vocab, _ = self.one_time_world_instance.downloadTrainingDataPlayer.add_stock_symbols_to_vocab(
                        vocab=vocab,
                        current_id=0,  
                )
        
```
とした形でデバッグ中


うーわーーー、、、
![Screenshot from 2024-11-25 05-45-32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3910368/473040ce-c0a1-4141-ca84-4694a2a6b6b8.png)
じゃあ設定だ、、、、わーーーっ、でもよかった、それだけでもわかって。

## 2024-11-25
- SetupGpt2Playerの確認
    - 辞書の作成時、改行を削除し忘れていた。
        - line = line.replace('\n','')
    を追加。
        学習のときも削除忘れてたら、問題だなぁ、、。最後の行以外のeosトークンだけ改行を含む語彙となって学習されていることになるから、、。
        うーん、でもこれは微々たることで、今回の問題ではないよね。
        うん、問題じゃなかった。変化なし(デバッグした)。
    - なんか学習時なのに
    self.predict_than_max_lengthを設定してる、、、

## 2024-11-26
GPT2の設定において、
- n-tcr
という項目について気になる。
これはAttentionLayer内の何かを定義しているようだ。
AttentionLayerはAttentionWeightLayerとWeightSumLayerの2つから出来ており、1position前の自身の隠れ重みを取ってくるLayerです。
過去にまとめたのがあるので貼ろう。
![IMG_0095.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3910368/4d6797f6-29f4-3616-cbf0-af61a5de9e37.jpeg)
ここで言う、Tサイズが、n-ctx、、
いやちょっと待ってよ。
Tサイズって、Encoderのポジション数じだから、n-ctxを小さくすれば、AI頭悪くなるけどメモリ抑えられるのは、理解できるけど、
え、GPT2ってself-Attentionじゃないの？Tサイズは自身のサイズになるんじゃないの、、？

と、言うことで勉強しました。

self-attentionによって、平行処理が可能になったけれど、
AttentionWeightの直前は、全てのポジションのスコアが算出されるまで、待ってから、各自身のポジションより左側にあるスコアを、各ポジションが集めて、それをhsとして、また各ポジションのLayerは最終レイヤまで動き始めるということ。
いったん足並み揃えるんだね。
つまり、こーゆーこと！！↓
![IMG_0097.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3910368/641a1afb-2520-2bb2-5e7f-e680c3f99f61.jpeg)
つまり
n-ctxは、ハード上の制約がない限り、ポジション数にすれば、過去の情報を最大限に学習することが出来る！

さらに、予めn-ctxを決めているので、統一するために、そのポジションでhsのhの数がn-ctxに満たない場合は、パディングをする。
つまり、こう！↓
![IMG_0100.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3910368/79a203e8-4411-9d37-136d-ae94549cd29f.jpeg)



しかし、これではまだGPT2ではない。
GPT2においてのAttentionLayerは、
ここで言うhsはさらに分割される。
hsが
```
<sos>: [1, 2, 9, 0]
私: [1, 2, 3, 4]
は: [5, 6, 7, 8]
```
ならば、
```
<sos>0: [1, 2]
<sos>1: [9, 0]

私0: [1, 2]
私1: [3, 4]

は0: [5, 6]
は1: [7, 8]
```
と分散され、
0と1はそれぞれで、AttentionLayerを通り、
出力では再度横に結合される。
つまり、こう！↓
![IMG_0099.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3910368/9dd04cff-ee3e-690f-abba-c86dbbe33f89.jpeg)

なお、逆伝播でもその流れで問題なし。

なお、今回のパディングについては、分かりやすく0としたが、本来は0ではなく-1e9など、とても小さい数字を用いる。0で掛けちゃうと全部0になっちゃうからね。


## 2024-11-28
つづき。
- initialize_weights の更新。

## 2024-11-28
```
（小声）
しらなかった、、
「
    def method(self, new_bos_token):
        self.bos_token = new_bos_token  # まず自分のインスタンスのbos_tokenを更新
        super().__init__(bos_token=new_bos_token)  # 親クラスのbos_tokenも更新
        return
」
シンクロしてないのね。

```


## SetupGpt2Playerの見直しが完了した。
一つ問題を発見した。
max_lengthが２つあった。
一つは推論するサイズ。
もう一つはx_size。
これをメンバ変数で定義しており、Gpt2TrainPlayerで読み込み間違えていたので、
predicting_size
x_max_length
として再定義した。
x_max_lengthに関しては、変数名をx_sizeとすると、なんだかtorch側と変数名がかぶりそうな心意気でしたので…。

- Gpt2TrainPlayerの確認
    - modelは問題なさそう、、。（過去と現在のmodelを直接観て比較。）

    - load_and_update()を大幅改良中。

"" 2024-12-06
今回、レイヤーごとに前回のモデルのパラメータを今回のモデルに移植してる。
この時、今回のモデルが前回の重み行列より１行多かった時、
```
                            (前回: {prev_param.shape}, 現在: {curr_param.shape})。\n\
                            強制的に上書きします。今回の行が多かった場合は\n\
                            ---\n\
                            [[1, 2, 3],    ← 前回の1行目をコピー\n\
                            [4, 5, 6],    ← 前回の2行目をコピー\n\
                            [13, 14, 15]] ← 残りはそのまま\n\
                            ---\n\
                            となります。\n")
```
としている。
が、これはEmbeddingLayerの重みの考慮である。
ここで、学習する語彙数が、前回より１つまたは１つ以上増える場合は、それに合わせて１行。２行と単語ベクトルが増える。

ココで確認したいのは、EmbeddingLayer作成時、新しい語彙の単語ベクトルが、重み行列的に下の行が新しい語彙なのか、または上の行ほど新しい単語ベクトルなのかを確認したい。

- EmbeddingLayerの重み作成時、新しい単語ベクトルは重み行列の上なのか下なのか確認する
良い情報を得た。pytorchに置いて、EmbeddingLayerの重み行列のindexは、tokenizerの語彙の辞書IDと一致しているらしい。
なので。前回のパラメータを今回のパラメータにコピーする時、Layerのshapeの不一致が確認された時点で、そのLayerがEmbeddingLayerだった場合、
まず、
- 前回の語彙辞書
- 今回の語彙辞書(ID値最大の語彙を除く)
が一致することを確認し、
さらに
- 前回の語彙辞書のIDが、しっかり連番になっている
ことを確認する必要がある。


- GPT2TrainPlayerの、パラメータ引き継ぎコードを改めて確認
EmbeddingLayerの重みの単語ベクトルサイズ、SettingPlayerから持ってこようよ。

さて。パラメータのコピーはうまくできた。
```可読性重視。

                        """ 強制上書き: パラメータを1次元化してコピー可能な部分のみ上書き
                        前回のパラメータ：
                        [[1, 2, 3],
                        [4, 5, 6]]  # torch.Size([2, 3])

                        現在のパラメータ：
                        [[7, 8, 9],
                        [10, 11, 12],
                        [13, 14, 15]]  # torch.Size([3, 3])

                        コピー後の結果：
                        [[1, 2, 3],    ← 前回の1行目をコピー
                        [4, 5, 6],    ← 前回の2行目をコピー
                        [13, 14, 15]] ← 残りはそのまま
                        """
                        # 前回パラメータ、今回パラメータ、一時両方フラットにする。
                        flattened_prev = prev_param.view(-1)  # 1次元に変換
                        flattened_curr = curr_param.view(-1)  # 1次元に変換
                        
                        # フラットしたデータのnumel(lenと思ってね)を返し、コピできる場所だけコピ。
                        min_size = min(flattened_prev.numel(), flattened_curr.numel())
                        flattened_curr[:min_size] = flattened_prev[:min_size]
                        
                        # 元の形状に戻して保存
                        new_param = flattened_curr.view_as(curr_param).clone()
                        new_state_dict[layer_name] = new_param

                        # 表示。
                        print("前回のパラメータ:")
                        new_param = pd.DataFrame(prev_param.detach().cpu().numpy())
                        print(new_param)  # 先頭の数行だけ表示
                        
                        print("\n現在のパラメータ:")
                        curr_param_df = pd.DataFrame(curr_param.detach().cpu().numpy())
                        print(curr_param_df)  # 先頭の数行だけ表示
                        
```
 
さて。
```
            # コピーしたパラメータを現在のモデルに反映
            input("まだnew_modelは用意してない。new_state_dictを使って０からnew_modelは作りたいから、あとで改良。")
            # new_model.load_state_dict(new_state_dict)

```
としておいた。input()で残しておけば後で気がつけるもんね。あったまいーじぶんっ！

とりあえず、次、トークナイザのに取り掛かる。
前回のトークナイザがあることを前提に、コピーを進めていく、、が。
コピーではなく、新しく作成する方向で行く。
今回カスタムトークナイザなので、カスタムトークナイザを作成しているメソッドをココに呼び出し、前回と今回を調和したカスタムトークナイザインスタンスを、メソッドを使用して直接作成しよう。その方が安全だ。

あ、日付、、
## 2024-12-09
カスタムトークナイザのメソッドの引数、なにがいるんだっけ、、観なきゃ。 

ウーン、
tokenizerの引数、


## 2024-12-12
なんだ！？
```

wte.weight の形状修正の詳細:
処理前の形状: torch.Size([87, 600])（前回のモデル）, torch.Size([105, 600])（現在のモデル）
処理後の重み:
前回のモデルの重み:          0         1         2         3         4         5         6         7         8         9         10        11        12        13   ...       586       587       588       589       590       591       592       593       594       595       596       597       598       599
0  -0.064303  0.092970 -0.032418  0.037699 -0.010181  0.023179  0.027050  0.068628 -0.012856 -0.045232  0.071818 -0.049973  0.033060  0.042962  ...  0.067140 -0.002276  0.017391 -0.041062  0.071168  0.033829  0.004268 -0.005421  0.012638  0.066751 -0.067922  0.078094  0.068133 -0.045032
1  -0.063870 -0.072182  0.038266 -0.003108 -0.034967 -0.089123 -0.067605 -0.013812  0.061172 -0.013333  0.090429 -0.062425  0.039199  0.016558  ...  0.045362  0.057041 -0.000093  0.031485  0.068884  0.069327  0.040355  0.027120 -0.000176  0.079329  0.029250  0.027446  0.059598  0.092885
2  -0.061988 -0.072596 -0.026153  0.022259 -0.055686  0.023497  0.081121  0.062443  0.030062  0.043766 -0.077535 -0.087223 -0.039217 -0.065626  ... -0.040526 -0.014890  0.024568  0.007464  0.002912 -0.077676  0.083046 -0.008832  0.093071  0.017163 -0.065952  0.017149  0.009391  0.059271
3  -0.045409  0.032169  0.002008  0.074071  0.028614 -0.009683  0.044408 -0.027397  0.049462  0.018948  0.071472 -0.075640  0.021492  0.037821  ... -0.058124  0.040744 -0.042320 -0.000601  0.061810 -0.040406 -0.090613  0.077970 -0.004440 -0.025540 -0.066416  0.047448  0.084758  0.031167
4  -0.018848 -0.080686  0.060635  0.079016  0.020186 -0.029609  0.035638  0.004501 -0.056577 -0.004097 -0.079142 -0.085290  0.051269  0.005404  ... -0.015582 -0.090049 -0.005160  0.030663  0.072844 -0.059439  0.061960 -0.088584  0.059122  0.018337 -0.060053 -0.003734  0.059317  0.082223
..       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...  ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...
82  0.060973 -0.040227 -0.004305  0.006504  0.082271  0.058341  0.046996  0.071392  0.050027 -0.055831  0.063745 -0.033887 -0.013022  0.008196  ...  0.036383  0.034126  0.068070  0.075538 -0.066459  0.051498 -0.008916  0.043844 -0.055571 -0.049660 -0.047099  0.075810  0.090953  0.007647
83 -0.029683 -0.055775  0.074959  0.087015 -0.045817  0.079207 -0.013053 -0.024545  0.064787  0.002986 -0.038189 -0.023019 -0.036378  0.062058  ... -0.078731  0.008116 -0.036564  0.075526  0.050419  0.037808 -0.003764  0.018968 -0.018449 -0.088047  0.037458 -0.047430 -0.078986  0.055582
84  0.048888 -0.005048 -0.056400  0.092562  0.045697  0.053290 -0.068811  0.076627  0.088604 -0.019326  0.036076 -0.048882  0.040444  0.057954  ... -0.084779 -0.011253 -0.090031 -0.075254  0.006937 -0.045478 -0.060392  0.087739  0.049524 -0.086480 -0.025774  0.043075 -0.042237 -0.062141
85 -0.055243  0.011872  0.001295 -0.023371  0.057951 -0.028119  0.058315 -0.000864  0.042823 -0.088287 -0.030957  0.028547  0.056757  0.071881  ...  0.073540 -0.056002 -0.039072  0.008377 -0.052121 -0.053138 -0.073646  0.069847  0.076661 -0.051202 -0.030266 -0.088897  0.013938 -0.031440
86  0.029445  0.050350 -0.058553  0.071016 -0.052645 -0.017374 -0.062596 -0.063441 -0.040474  0.037164 -0.018625  0.017324 -0.018601 -0.016077  ... -0.082335 -0.044339 -0.019255  0.067011 -0.002779 -0.020268 -0.037077  0.071199 -0.030834  0.086615  0.019859 -0.069983 -0.005150 -0.022765

[87 rows x 600 columns]
現在のモデルの重み:           0         1         2         3         4         5         6         7         8         9         10        11        12        13   ...       586       587       588       589       590       591       592       593       594       595       596       597       598       599
0   -0.064303  0.092970 -0.032418  0.037699 -0.010181  0.023179  0.027050  0.068628 -0.012856 -0.045232  0.071818 -0.049973  0.033060  0.042962  ...  0.067140 -0.002276  0.017391 -0.041062  0.071168  0.033829  0.004268 -0.005421  0.012638  0.066751 -0.067922  0.078094  0.068133 -0.045032
1   -0.063870 -0.072182  0.038266 -0.003108 -0.034967 -0.089123 -0.067605 -0.013812  0.061172 -0.013333  0.090429 -0.062425  0.039199  0.016558  ...  0.045362  0.057041 -0.000093  0.031485  0.068884  0.069327  0.040355  0.027120 -0.000176  0.079329  0.029250  0.027446  0.059598  0.092885
2   -0.061988 -0.072596 -0.026153  0.022259 -0.055686  0.023497  0.081121  0.062443  0.030062  0.043766 -0.077535 -0.087223 -0.039217 -0.065626  ... -0.040526 -0.014890  0.024568  0.007464  0.002912 -0.077676  0.083046 -0.008832  0.093071  0.017163 -0.065952  0.017149  0.009391  0.059271
3   -0.045409  0.032169  0.002008  0.074071  0.028614 -0.009683  0.044408 -0.027397  0.049462  0.018948  0.071472 -0.075640  0.021492  0.037821  ... -0.058124  0.040744 -0.042320 -0.000601  0.061810 -0.040406 -0.090613  0.077970 -0.004440 -0.025540 -0.066416  0.047448  0.084758  0.031167
4   -0.018848 -0.080686  0.060635  0.079016  0.020186 -0.029609  0.035638  0.004501 -0.056577 -0.004097 -0.079142 -0.085290  0.051269  0.005404  ... -0.015582 -0.090049 -0.005160  0.030663  0.072844 -0.059439  0.061960 -0.088584  0.059122  0.018337 -0.060053 -0.003734  0.059317  0.082223
..        ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...  ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...
100 -0.025605 -0.000768 -0.087061 -0.063698 -0.005337 -0.054840  0.057069 -0.006601  0.041168 -0.015780 -0.038685  0.033154  0.003840  0.018210  ...  0.081044  0.028271 -0.091383  0.055066 -0.005035  0.062456 -0.033092 -0.041889 -0.040004 -0.023530 -0.089489 -0.027455  0.059841  0.090071
101  0.055280  0.036507 -0.071197  0.026549 -0.042291 -0.011551 -0.004498  0.010253  0.059902 -0.009673  0.083752 -0.006133 -0.068220 -0.071661  ...  0.054587 -0.020004 -0.009714 -0.062803 -0.070055  0.068903  0.012342  0.079202 -0.041975 -0.052979  0.038930  0.064427  0.044971  0.025408
102  0.059585 -0.091859 -0.067745 -0.017255 -0.057225 -0.072977 -0.063723 -0.066441 -0.041732 -0.007293  0.074955 -0.017373 -0.022864  0.011957  ...  0.055489  0.053195  0.060773 -0.007401 -0.063297  0.061571  0.021888  0.032238  0.016006 -0.028834 -0.066089 -0.046701 -0.074448  0.090144
103 -0.055568  0.019707  0.056154 -0.084041  0.021436 -0.084714  0.059142  0.070338 -0.067109 -0.065540 -0.046469  0.000456  0.023554 -0.040485  ... -0.030201  0.042173  0.039290  0.044774 -0.054842 -0.068663  0.075466  0.001803  0.014187 -0.083994 -0.092251  0.075090  0.055346 -0.039557
104 -0.035336 -0.028551  0.007674 -0.013765  0.042289 -0.065073 -0.019088  0.053110  0.087377 -0.081528 -0.061548 -0.060002 -0.034828 -0.019991  ...  0.080935  0.087051  0.019942 -0.081036  0.014874  0.019951 -0.030568 -0.069208 -0.060895 -0.084505  0.011286  0.082573 -0.088699  0.061816

```

headとweightでパラメータが異なっている。

train更新時に、片方しか更新していないミスをしているか、今確認する。

更新直後にデバッグしてみたが、問題なし。
![Screenshot from 2024-12-12 18-14-38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3910368/fd4aaf4d-f90d-7c8d-47ff-a82a9e622814.png)
保存時に問題があるのでしょうか。


あー、もしかして。
この学習後、保存する前に検証ステップをデバッグとして入れてるのですが…
![Screenshot from 2024-12-12 18-20-14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3910368/bfd262a6-b4fa-425a-c73a-217cae28d2aa.png)
この時torch.no_grad()を実装していないから、推論後、、、なにかパラメータが変化してしまっているのかな。

検証ステップ後に、EmbeddingLayerの重みを確かめてみよう。
![Screenshot from 2024-12-12 18-23-36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3910368/922b60ff-7884-8681-827d-fd71f4e46449.png)
一致してるんかーい

でも心配なので
```

            # 検証ステップ
            self.model.eval()  # モデルを評価モードに設定
            total_val_loss = 0
            

            for batch in validationLoader:
                inputs = batch['input_ids'].to(self.model.device)
                labels = batch['labels'].to(self.model.device)

                # フォワードパス（検証時はバックワードなし）
                with torch.no_grad(): # 勾配計算を省き、パラメータの無変化を保証
                    outputs = self.model(input_ids=inputs, labels=labels)
                    loss = outputs.loss

                total_val_loss += loss.item()

            print(f"エポック {epoch + 1} 後の検証損失: {total_val_loss / len(validationLoader)}")

```
として、勾配の計算は絶対しないようにした。


じゃぁ、
・保存する前のパラメータ
・追加学習時に取得している、前回のパラメータ
この２つが、一致するかどうかを確認してみよう。
一致していた。

## 2024-12-13 
（日付書くの忘れちゃう、、）
追加学習時のデータ用意タイミングでデバッグしよう。
ターミナル２つ開いて、

おお、一緒じゃないですか。
![Screenshot from 2024-12-13 19-30-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3910368/7e545b80-8e6c-ab3b-286d-6dc35483c68a.png)
ん、あれっ、、、？
３つある！

- transformer.wte.weight
- lm_head.weight
- wte.weight

なんだろう、、
ワタクシの疑問は、
```
EmbeddinLayerの重みは、単語表現ベクトル群でしょう。
そして、GPT2では、出力層ではEmbeddingLayerの重みを直接参照しているわけではなく、わざわざEmbeddingLayerと同じ重みをもたせたLayerを作り、そのLayerから重みを参照して、次のポジションの語彙を予測しているみたい。

なので、追加学習のときは、前回のパラメータと今回のパラメータで異なるパラメータは、２Layerのみだと思っていたよ。しかしデバッグすると、

- transformer.wte.weight
- lm_head.weight
- wte.weight

３つのLayerのパラメータが異なると言われたの。
ちょっと、どれがEmbeddingLayerなのか、そしてEmbeddingLayerと同じ重みを持ったLayerなのか、そして、もう一つのLayerは一体何者なのか。
```

ChatGPTにきいてきた。
```
- transformer.wte.weight
これが真のEmbedding Layer

- lm_head.weight
これは、出力層用

- wte.weight
、、、本来あるのは可笑しい。GPT4に謎って言われた。

```

すべてのLayerを観てみると、次のようになる。
```
最初の方は、
「


transformer.wte.weight の形状修正の詳細:
処理前の形状: torch.Size([87, 600])（前回のモデル）, torch.Size([93, 600])（現在のモデル）
処理後の重み:
前回のモデルの重み:          0         1         2         3         4         5         6         7         8         9    ...       590       591       592       593       594       595       596       597       598       599
0  -0.103373  0.078662 -0.129817  0.066639 -0.156473 -0.302803 -0.014642  0.007430  0.012751  0.105480  ... -0.057228  0.080301 -0.140664  0.054861 -0.156028  0.132478  0.171036 -0.006771  0.130464  0.009277
1  -0.110146 -0.123749  0.096287  0.107202 -0.053872  0.145189  0.251104  0.199932 -0.046281  0.001416  ... -0.055567  0.187496 -0.159806 -0.081558 -0.263859 -0.123064  0.245114  0.014365  0.257943 -0.085046
2  -0.157198 -0.079342  0.123973 -0.030474  0.012136  0.080994 -0.106337  0.147913  0.126997 -0.112352  ...  0.040816  0.010468  0.140485 -0.090364  0.046677 -0.136191  0.017676 -0.086803 -0.161165  0.078340
3   0.097002  0.031817 -0.030388 -0.031676  0.014608 -0.069121 -0.042859 -0.020572 -0.050826 -0.003538  ...  0.018863  0.029705 -0.087106  0.020267  0.032355  0.054769 -0.016103 -0.091969  0.089973 -0.023631
4   0.041637 -0.115818 -0.065558 -0.168347  0.062169 -0.028026  0.011111 -0.077708  0.106249 -0.093372  ... -0.007564 -0.136615  0.091134  0.002299  0.014689 -0.061722  0.112189  0.167426  0.026314  0.093644
..       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...  ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...
82  0.015786 -0.015566  0.007491 -0.005115 -0.108542 -0.070074 -0.004650 -0.079316 -0.082713 -0.008952  ... -0.049457 -0.004675 -0.072287  0.129581  0.030859  0.060691 -0.128443 -0.048904  0.034666 -0.012595
83 -0.091307  0.074969  0.005081  0.071425 -0.303376 -0.205155  0.102324 -0.090576 -0.014442  0.116375  ... -0.199030  0.094682 -0.043750  0.007990 -0.122881  0.135915  0.063540  0.011488  0.106818  0.011106
84 -0.179312  0.180885 -0.065383  0.070793 -0.196920 -0.287011 -0.001115  0.035364  0.011113  0.039221  ... -0.219794  0.071402 -0.114766  0.045513 -0.077194  0.050629  0.195593 -0.012899  0.014920 -0.050243
85 -0.138082  0.104597 -0.155916 -0.010006 -0.227684 -0.240494  0.031815 -0.004094 -0.168970  0.132346  ... -0.179397  0.029149 -0.073160  0.014602 -0.144013  0.012519  0.191334 -0.070982  0.046907  0.021804
86 -0.061566  0.067472 -0.101890  0.058137 -0.250545 -0.177638  0.054229 -0.001760 -0.163243  0.129101  ... -0.050015  0.104700 -0.092465  0.090777 -0.066448  0.080964  0.143129 -0.011160  0.107742 -0.008622

[87 rows x 600 columns]
うぇたえとぁ
レイヤー transformer.wpe.weight をコピーしました: 形状 torch.Size([149, 600])
レイヤー transformer.h.0.ln_1.weight をコピーしました: 形状 torch.Size([600])
レイヤー transformer.h.0.ln_1.bias をコピーしました: 形状 torch.Size([600])
レイヤー transformer.h.0.attn.bias をコピーしました: 形状 torch.Size([1, 1, 149, 149])
レイヤー transformer.h.0.attn.masked_bias をコピーしました: 形状 torch.Size([])
レイヤー transformer.h.0.attn.c_attn.weight をコピーしました: 形状 torch.Size([600, 1800])
レイヤー transformer.h.0.attn.c_attn.bias をコピーしました: 形状 torch.Size([1800])
レイヤー transformer.h.0.attn.c_proj.weight をコピーしました: 形状 torch.Size([600, 600])
レイヤー transformer.h.0.attn.c_proj.bias をコピーしました: 形状 torch.Size([600])
レイヤー transformer.h.0.ln_2.weight をコピーしました: 形状 torch.Size([600])
レイヤー transformer.h.0.ln_2.bias をコピーしました: 形状 torch.Size([600])
レイヤー transformer.h.0.mlp.c_fc.weight をコピーしました: 形状 torch.Size([600, 2400])
レイヤー transformer.h.0.mlp.c_fc.bias をコピーしました: 形状 torch.Size([2400])
レイヤー transformer.h.0.mlp.c_proj.weight をコピーしました: 形状 torch.Size([2400, 600])

」
だね。おそらく真のEmbeddinLayer層だよね。
問題は、出力層付近。
「
レイヤー transformer.h.74.ln_2.weight をコピーしました: 形状 torch.Size([600])
レイヤー transformer.h.74.ln_2.bias をコピーしました: 形状 torch.Size([600])
レイヤー transformer.h.74.mlp.c_fc.weight をコピーしました: 形状 torch.Size([600, 2400])
レイヤー transformer.h.74.mlp.c_fc.bias をコピーしました: 形状 torch.Size([2400])
レイヤー transformer.h.74.mlp.c_proj.weight をコピーしました: 形状 torch.Size([2400, 600])
レイヤー transformer.h.74.mlp.c_proj.bias をコピーしました: 形状 torch.Size([600])
レイヤー transformer.ln_f.weight をコピーしました: 形状 torch.Size([600])
レイヤー transformer.ln_f.bias をコピーしました: 形状 torch.Size([600])

レイヤー lm_head.weight の形状が一致しませんでした。shapeは次のとおりです。
                            強制的に上書きします。今回の行が多かった場合は
                            ---
                            [[1, 2, 3],    ← 前回の1行目をコピー
                            [4, 5, 6],    ← 前回の2行目をコピー
                            [13, 14, 15]] ← 残りはそのまま
                            ---
                            となります。


lm_head.weight の形状修正の詳細:
処理前の形状: torch.Size([87, 600])（前回のモデル）, torch.Size([93, 600])（現在のモデル）
処理後の重み:
前回のモデルの重み:          0         1         2         3         4         5         6         7         8         9    ...       590       591       592       593       594       595       596       597       598       599
0  -0.103373  0.078662 -0.129817  0.066639 -0.156473 -0.302803 -0.014642  0.007430  0.012751  0.105480  ... -0.057228  0.080301 -0.140664  0.054861 -0.156028  0.132478  0.171036 -0.006771  0.130464  0.009277
1  -0.110146 -0.123749  0.096287  0.107202 -0.053872  0.145189  0.251104  0.199932 -0.046281  0.001416  ... -0.055567  0.187496 -0.159806 -0.081558 -0.263859 -0.123064  0.245114  0.014365  0.257943 -0.085046
2  -0.157198 -0.079342  0.123973 -0.030474  0.012136  0.080994 -0.106337  0.147913  0.126997 -0.112352  ...  0.040816  0.010468  0.140485 -0.090364  0.046677 -0.136191  0.017676 -0.086803 -0.161165  0.078340
3   0.097002  0.031817 -0.030388 -0.031676  0.014608 -0.069121 -0.042859 -0.020572 -0.050826 -0.003538  ...  0.018863  0.029705 -0.087106  0.020267  0.032355  0.054769 -0.016103 -0.091969  0.089973 -0.023631
4   0.041637 -0.115818 -0.065558 -0.168347  0.062169 -0.028026  0.011111 -0.077708  0.106249 -0.093372  ... -0.007564 -0.136615  0.091134  0.002299  0.014689 -0.061722  0.112189  0.167426  0.026314  0.093644
..       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...  ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...
82  0.015786 -0.015566  0.007491 -0.005115 -0.108542 -0.070074 -0.004650 -0.079316 -0.082713 -0.008952  ... -0.049457 -0.004675 -0.072287  0.129581  0.030859  0.060691 -0.128443 -0.048904  0.034666 -0.012595
83 -0.091307  0.074969  0.005081  0.071425 -0.303376 -0.205155  0.102324 -0.090576 -0.014442  0.116375  ... -0.199030  0.094682 -0.043750  0.007990 -0.122881  0.135915  0.063540  0.011488  0.106818  0.011106
84 -0.179312  0.180885 -0.065383  0.070793 -0.196920 -0.287011 -0.001115  0.035364  0.011113  0.039221  ... -0.219794  0.071402 -0.114766  0.045513 -0.077194  0.050629  0.195593 -0.012899  0.014920 -0.050243
85 -0.138082  0.104597 -0.155916 -0.010006 -0.227684 -0.240494  0.031815 -0.004094 -0.168970  0.132346  ... -0.179397  0.029149 -0.073160  0.014602 -0.144013  0.012519  0.191334 -0.070982  0.046907  0.021804
86 -0.061566  0.067472 -0.101890  0.058137 -0.250545 -0.177638  0.054229 -0.001760 -0.163243  0.129101  ... -0.050015  0.104700 -0.092465  0.090777 -0.066448  0.080964  0.143129 -0.011160  0.107742 -0.008622

[87 rows x 600 columns]
うぇたえとぁ

レイヤー wte.weight の形状が一致しませんでした。shapeは次のとおりです。
                            強制的に上書きします。今回の行が多かった場合は
                            ---
                            [[1, 2, 3],    ← 前回の1行目をコピー
                            [4, 5, 6],    ← 前回の2行目をコピー
                            [13, 14, 15]] ← 残りはそのまま
                            ---
                            となります。


wte.weight の形状修正の詳細:
処理前の形状: torch.Size([87, 600])（前回のモデル）, torch.Size([93, 600])（現在のモデル）
処理後の重み:
前回のモデルの重み:          0         1         2         3         4         5         6         7         8         9    ...       590       591       592       593       594       595       596       597       598       599
0  -0.062030 -0.004282 -0.033645 -0.082841  0.050725  0.019846  0.052532 -0.045424 -0.079969 -0.043201  ... -0.067210  0.067529  0.024055  0.007138 -0.067790  0.072241  0.078416  0.000090  0.001194 -0.085155
1  -0.033831 -0.075529 -0.036555 -0.049714 -0.063507 -0.009617  0.042845  0.063401 -0.060978 -0.019390  ...  0.047136  0.089294 -0.006941 -0.064044 -0.021120  0.016256 -0.010972  0.027543  0.067350 -0.093443
2   0.083980 -0.023379  0.047921  0.088719 -0.031362 -0.045095 -0.007201 -0.049125 -0.092821 -0.050885  ...  0.071122  0.003551 -0.062358 -0.072521 -0.009826  0.035529  0.048420 -0.046510  0.049076  0.026310
3   0.015647  0.036005 -0.039654 -0.020582 -0.030557 -0.076351 -0.040802 -0.084293 -0.069950  0.042393  ... -0.034191  0.036693  0.079556 -0.040363  0.027209 -0.024405 -0.014170 -0.028645 -0.058441 -0.003379
4   0.048290  0.076011 -0.062981  0.019830  0.019608 -0.038274  0.077146  0.080186 -0.082791  0.076291  ... -0.077169 -0.068112  0.043999  0.011569 -0.075490 -0.048825 -0.018820 -0.066232  0.088763  0.005748
..       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...  ...       ...       ...       ...       ...       ...       ...       ...       ...       ...       ...
82  0.049249 -0.014719 -0.080009 -0.012861 -0.065629  0.043696 -0.011546 -0.038698  0.042269 -0.073895  ... -0.055296 -0.063558 -0.056011 -0.055749  0.012933  0.061220 -0.056600  0.090807 -0.032244 -0.053612
83  0.064432  0.007737 -0.065089  0.057687  0.083954 -0.048550  0.075896 -0.009634 -0.006159 -0.076421  ... -0.011386  0.081424  0.063344  0.074665  0.000031 -0.056182 -0.024693  0.092415 -0.042232  0.047369
84 -0.006023 -0.053880  0.051998 -0.039184  0.066127  0.000739  0.056420 -0.016619  0.013359 -0.038341  ... -0.020795 -0.006230 -0.035993  0.059802 -0.002159  0.067780 -0.056947  0.069852  0.077798 -0.016161
85  0.077600  0.020641  0.075225  0.092001  0.039485 -0.023327 -0.060242 -0.087358 -0.003355  0.014599  ... -0.081254  0.000252  0.057974  0.033608 -0.000842 -0.023099 -0.012374  0.085154  0.069809  0.071615
86 -0.043909  0.027762 -0.029013 -0.004246 -0.039098  0.084750 -0.033154 -0.084345  0.010319 -0.075567  ...  0.080709  0.058832 -0.070758  0.037645 -0.019223  0.080354  0.072557  0.058604 -0.027576 -0.082658

[87 rows x 600 columns]

」
一番最後に存在するみたい。
```


さて、どうしようか。
model変数をずっとさかのぼって、どこからwte.weightが存在するのか、確認していこう。
遡ると言うか、SetupGpt2Playerを観てみようかな、一番怪しい

さて。全てのLayerを確認するコードは、GPT2TrainPlayerのメソッドとして定義しておこう。たくさん使いそう。引数は、model一つだけでいいや。

## 2024-12-15
ちょっとGPT頼りなんだけれども、
![Screenshot from 2024-12-15 03-29-36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3910368/a4c77d06-5548-83a4-e1a9-a1d2599b18e9.png)
特殊トークンを設定した時に、新しいレイヤーが追加される可能性があるらしい。



っしゃ！！！見つけた！！！
```

        # モデルの初期化
        self.model = GPT2LMHeadModel(config)
        """ ここまでで、modelを作ってる """

        display_model_layers_with_embedding_details(self.model)
        input("masarina5453w35")


        # Embedding Layerの初期化（単語表現二次元ベクトルの作成（0で初期化））
        self.model = self.initialize_embedding_layer(self.model, vocab_size) # 行列(vocab_size, model.config.n_embd) の単語表現ベクトルを作成で、０で初期化。


        display_model_layers_with_embedding_details(self.model)
        input("まさりなてｓて")



```
初期化してるのがイケなかった。

このデバッグをする過程で
```
        

def display_model_layers_with_embedding_details(model):
    """
    Display the layer names of the model. If the layer is related to EmbeddingLayer,
    its weights will be displayed in detail as a pandas DataFrame.

    Args:
        model: PyTorch model whose layers and weights need to be displayed.

    Returns:
        None
    """
    print("Model Layers Details:")
    for layer_name, layer_weights in model.state_dict().items():
        if layer_name in ["transformer.wte.weight", "lm_head.weight", "wte.weight"]:
            print(f"Layer Name: {layer_name}")
            # Convert weights to pandas DataFrame for better visualization
            weights_df = pd.DataFrame(layer_weights.detach().cpu().numpy())
            print(f"Weights as DataFrame:\n{weights_df}")
        else:
            print(f"Layer Name: {layer_name} (Weights are not displayed for non-Embedding layers)")
        print("-" * 50)


```
という関数を作成して、デバッグしていった。


「
        # Embedding Layerの初期化（単語表現二次元ベクトルの作成（0で初期化））
        self.model = self.initialize_embedding_layer(self.model, vocab_size) # 行列(vocab_size, model.config.n_embd) の単語表現ベクトルを作成で、０で初期化。

」
この初期化については、覚えている。シャビアーの初期化（読み方あってるのかな）をする前に、初期化した胃レイヤーのパラメータを、明示的に０にし、
パラメータが全て０のものだけ


        # 全ての重みの要素を0で初期化
        self.model = self.initialize_weights_with_zeros(model=self.model)

        # 全ての層の重みをXavier初期化（1次元以上、かつ数値が全て0のベクトルを初期化）
        self.model = self.initialize_weights(self.model)

このように初期化しようと思ったんだった。


さて。直してぞ。

そもそも私はなにをデバッグしていたんだっけ。タシカ、なにかをデバッグしていて、このEmbeddng問題にぶつかったのだよ。

追加学習する時に、問題があったよね。そうだ、追加学習する時、学習自体がされなかったんだ。
学習データ、検証データともに、いくら追加学習しても、勾配の数値に変化が起きなかった。それなのにエラーにならないから不思議だったんだ。
今回の修正で治ってくれるだろうか、、。

学習、追加学習をして、確認をする。
(邪心発散：爪長くてキーボードが打てない、、切ってくる、、、)

## 2024-12-16
全然ダメ。
Optimizerを、引き継ぎをやめてその時のを使っても、だめ。

デバッグ方法が難しいので、ちょっと考えよう。

- じゃぁ、学習してる時の重みを直接デバッグして目視しよう。1度、本当に更新されていないのかを、学習時点で確認したい。
    ▶︎ この方法は一旦置いておこう

- 追加学習モードには自動で移行している。1度、前回のデータがあったとしても、前回のパラメータをロードしないようにしてみよう。それでもLoss問題が解決しなければ…。
    ![IMG_0213.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3910368/b7ff70cb-8631-997d-e98e-79b10cc33332.jpeg)
    変わる！▶︎ということは、model,tokenizer, optimizerのどれかが、おかしい。

- なにか1つの機能(例えばtokenizerのみ前回のをロードしない)のみを、ロードをやめて、なんの機能が学習を阻んでいるのかを特定する
    きたーっ！！！
    modelのみ、引き継ぎをやめたら、更新が進んだ！！
    ![IMG_0214.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3910368/ae380730-19fb-ad2e-095a-fe5870aea7e4.jpeg)
    ▶︎と、言うことはもちろん、modelの引き継ぎが出来ていない！
    ようやくたどり着いた…。

#### modelの修正に取り掛かろう
- カレントモデルをdeepcopyしてるの怪しいらしい(確かに。)
- 後、オプティマイザとモデルの同期が必要らしい。(同期ってなんのことよ)
    ```
    model = torch.load(previous_model_path)
    optimizer = torch.optim.AdamW(model.parameters(), lr=0.001)  # まずオプティマイザを初期化
    checkpoint = torch.load(previous_optimizer_path)  # セーブされた状態をロード
    optimizer.load_state_dict(checkpoint['optimizer_state_dict'])  # 状態を復元
    ```
    こんな感じがいいらしい
    いやいやさらに、なんとオプティマイザにパラメータ渡さなきゃ行けないらしい！これやってない！
    ![IMG_0219.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3910368/6622b29b-cb5f-7308-9974-9737168fe64c.jpeg)
    …意外とオプティマイザ、結構パラメータ持ってるのかも、、だってoptimizerファイルは
    ```
        # オプティマイザの状態を保存
        torch.save({
            'epoch': epoch + 1,  # 現在のエポックを保存
            'optimizer_state_dict': optimizer.state_dict()
        }, optimizer_save_path)
        print(f"オプティマイザの状態が {optimizer_save_path} に保存されました。")
    ```
    これだけで2.6GBあるもん。
    絶対更新式だけじゃないでしょう、笑
    このdictの中身をみてみよう。
    セーブ前に全てのオプティマイザの中身確認してみなきゃ、、


## 2024-12-17
modelは、バイアスと重みを持っているのね。
optimizerにも、ニューラルネットの情報をもたせる必要があって、でもそれは重みとかではなくて、AdamWのメタデータね？？
![Screenshot from 2024-12-17 20-02-44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3910368/2548f677-8457-4ec5-0adb-1d9f2521acbd.png)
![Screenshot from 2024-12-17 20-03-07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3910368/146bfb38-fcd2-96ac-04ce-f93946801104.png)