LLMの学習方法(事前学習、FineTuning, RLHF)

LLMの学習方法は大きく分けて3つある

1. 事前学習(Pre-training)
目的:
「言語の基礎的な知識」を身につける段階
文法・語彙・一般常識・文脈の理解などを、膨大なテキストから学ぶ

方法:
教師なし学習(Self-supervised learning)
入力文の一部を隠して、隠された部分を予測する(=マスク言語モデル、例:BERT)
次の単語を予測する(=自己回帰モデル、例:GPT系列)

データ:
インターネット上のテキスト(Webページ、書籍、Wikipediaなど)
数百億〜数兆トークン規模

2. ファインチューニング(Fine-tuning)
目的:
事前学習で得た「言語の基礎力」を、特定の目的や用途に合わせて調整

方法:
教師あり学習(Supervised fine-tuning)
入力と正解(出力)のペアを与える。
例:「質問 → 回答」「命令 → 応答」
少量の高品質データで調整。

3.人間のフィードバックによる強化学習(RLHF: Reinforcement Learning from Human Feedback)
目的:
モデルの出力を「人間にとって好ましい形」にする。
(=有用で、正確で、安全で、自然な応答にする)

① SFTモデル(ファインチューニング済)を使って、複数の応答候補を生成。
② 人間の評価者が「どの回答が良いか」を順位付け。
**③ 報酬モデル(Reward Model)**を学習。
→ 「この応答が好ましい」と判断するスコア関数を作る。
**④ 強化学習(PPOなど)**でモデルを更新し、報酬が高い出力を出すように最適化。

### Pretraining

import torch
import torch.nn as nn
import torch.optim as optim

text = "I love AI . AI loves people ."
vocab = list(set(text.split()))
word2idx = {w:i for i,w in enumerate(vocab)}
idx2word = {i:w for w,i in word2idx.items()}

def make_data(text):
    words = text.split()
    X, Y = [], []
    for i in range(len(words)-2):
        X.append([word2idx[words[i]], word2idx[words[i+1]]])
        Y.append(word2idx[words[i+2]])
    return torch.tensor(X), torch.tensor(Y)


X, Y = make_data(text)

class TinyLM(nn.Module):
    def __init__(self, vocab_size, hidden_size=8):
        super().__init__()
        self.emb = nn.Embedding(vocab_size, hidden_size)
        self.fc = nn.Linear(hidden_size*2, vocab_size)

    def forward(self, x):
        x = self.emb(x).view(x.size(0), -1)
        return self.fc(x)

model = TinyLM(len(vocab))
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.01)

for epoch in range(200):
    output = model(X)
    loss = criterion(output, Y)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

test = torch.tensor([[word2idx["I"], word2idx["love"]]])
pred = torch.argmax(model(test), dim=1)
print("Predicted next word:", idx2word[pred.item()])

### Fine tuning

train_data = [
    ("Hello", "Hi there!"),
    ("How are you?", "I'm fine, thank you."),
    ("Bye", "Goodbye!")
]

from transformers import AutoTokenizer, AutoModelForCausalLM, Trainer, TrainingArguments

tokenizer = AutoTokenizer.from_pretrained("gpt2")
model = AutoModelForCausalLM.from_pretrained("gpt2")

# データを整形
inputs = [tokenizer(f"User: {q}\nAI:", return_tensors="pt") for q, _ in train_data]
labels = [tokenizer(a, return_tensors="pt")["input_ids"] for _, a in train_data]

# 学習っぽいループ
for i, (inp, lab) in enumerate(zip(inputs, labels)):
    out = model(**inp, labels=lab)
    loss = out.loss
    loss.backward()
print("Fine-tuning step complete ✅")

### RLHF

import random

# モデルの応答候補
responses = ["Sure, here’s an example.", "I don’t know.", "You’re stupid."]

# 人間が選んだランキング(良い順)
human_ranking = {"Sure, here’s an example.": 1, "I don’t know.": 0, "You’re stupid.": -1}

# 報酬モデル(スコア関数)を模擬
def reward(response):
    return human_ranking[response]

# 強化学習っぽい更新(高報酬応答を強化)
policy = {r: 0.0 for r in responses}
for _ in range(10):
    r = random.choice(responses)
    policy[r] += reward(r) * 0.1

print("Learned policy:", policy)

[android]Composeのanimate関数

class MainActivity : ComponentActivity() {

    overrider fun onCreate(savedInstanceState: Bundle?){
        super.onCreate(saveInstanceState)
        setContent {
            MaterialTheme {
                AnimatedBoxExample()
            }
        }
    }

    @Composable
    fun AnimatedBoxExample() {
        var expaned by remember {mutableStateOf(false)}

        val boxSize by animateDpAsState(
            targetValue = if(expanded) 200.dp else 100.dp,
            label ="BoxSizeAnimation"
        )

        Column(
            modifier = Modifier
                .fillMaxSize()
                .padding(32.dp),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Box(
                modifier = Modifier
                    .size(boxSize)
                    .padding(8.dp),
                contentAlignment = Alignment.Center
            ) {
                color = MaterialTheme.colorScheme.primary,
                modifier = Modifier.fillMaxSize()
            } {}
        }
        Spacer(modifier = Modifier.height(16.dp))

        Button(onClick= {expanded = !expanded}) {
            Text(text =if (expanded) "縮小する" else "拡大する")
        }
    }
}

animateDpAsState:Dp 値を滑らかにアニメーションさせます。
targetValue:アニメーションで変化させたい目標値。
expanded を切り替えることで、Boxのサイズが 100.dp ↔ 200.dp にアニメーション付きで変化。

[LLM] トランスフォーマー構造の概要とAttentionの考え方

## 1. トランスフォーマーとは?
トランスフォーマーは2017年に登場した自然言語処理のニューラルネットワーク構造
RNNやCNNと違って系列データの処理を並列化できるのが最大の特徴
– 入力: 文章(単語やトークンに分けたもの)
– 出力: 文章の予測や分類など
– 主な構成:
1. エンコーダ: 入力を内部表現に変換
2. デコーダ: 内部表現から出力を生成
LLM(ChatGPTなど)は、基本的にデコーダ中心のトランスフォーマを使っている

## Attentionの考え方
Attentionは「文章の中でどの単語に注目すべきかを学習する仕組み」
文章: 「猫がネズミを追いかける」
「追いかける」を予測するとき、
「猫」が主語だから重要
「ネズミ」も目的語として重要
他の単語(「が」や「を」)はそれほど重要ではない
これを 数値化して重み付け するのが Attention の仕組み

## Self-Attentionの仕組み
トランスフォーマーでは 各単語が文章の他の単語を参照して特徴を作る
これを Self-Attention と呼ぶ

Query (Q):注目するための質問
Key (K):候補の特徴
Value (V):候補の情報

Query と Key の内積 → 「どれくらい注目すべきか」を示すスコア
Softmax で正規化 → 重みを 0〜1 に
Value に重みをかけて足し合わせ → 注目した情報の合成

## Multi-Head Attention
Attention は 1種類の見方だけでなく、複数の異なる視点で注目することができます。
これを Multi-Head Attention と呼ぶ

例:
1つ目のヘッド:主語と動詞の関係に注目
2つ目のヘッド:目的語と動詞の関係に注目
これにより、文脈を多面的に理解できる

なるほど~

[iOS/Swift] ネットワーク通信

swiftでネットワーク通知を学ぶ
– URLSession でAPIを叩く(例:天気アプリでAPIから天気データを取得)
– JSONデータのデコード(Codable)

対象API
https://api.open-meteo.com/v1/forecast?latitude=35.6895&longitude=139.6917&current_weather=true

データ

{
    "latitude":35.6895,
    "longitude":139.6917,
    "generationtime_ms":0.234,
    "utc_offset_seconds":0,
    "current_weather":{
        "temperature":28.3,
        "windspeed":5.2,
        "winddirection":90
    }
}

Swiftの場合だと、New Group で新規 Views, Modelsなどのディレクトリを作成する
– Views:画面用の SwiftUI View ファイルを入れる
– Models:JSON の Codable 構造体やデータモデルを入れる
models/Weather.swift

import Foundation

struct WeatherResponse: Codable {
    let current_weather: CurrentWeather
}

struct CurrentWeather: Codable {
    let temperature: Double
    let windspeed: Double
    let winddirection: Double
}

ContentView

import SwiftUI

struct ContentView: View {
    @State private var temperature: String = "__"
    @State private var windspeed: String = "__"
    
    var body: some View {
        VStack(spacing: 20) {
            Text("東京の現在天気")
                .font(.title)
            
            Text("気温: \(temperature)°C")
            Text("風速: \(windspeed)m/s")
            
            Button("天気を取得") {
                fetchWeather()
            }
        }
        .padding()
    }
    
    func fetchWeather() {
        guard let url = URL(string: "https://api.open-meteo.com/v1/forecast?latitude=35.6895&longitude=139.6917&current_weather=true") else { return }
        URLSession.shared.dataTask(with: url) { data, response, error in
            if let data = data {
                do {
                    let decoded = try JSONDecoder().decode(WeatherResponse.self, from: data)
                    DispatchQueue.main.async {
                                            self.temperature = String(decoded.current_weather.temperature)
                                            self.windspeed = String(decoded.current_weather.windspeed)
                                        }
                } catch {
                    print("JSONデコードエラー: \(error)")
                }
            } else if let error = error {
                print("通信エラー: \(error)")
            }
        }.resume()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

うおおおおおおおお、何だこれは すげえ

ポイント
– URLSession.shared.dataTask で非同期に API を叩く
– Codable で JSON を Swift の構造体にデコード
– DispatchQueue.main.async で UI 更新
– @State で変数をバインドして、UI に表示

[Figma] デザインを効率化・再利用する仕組みコンポーネント

### コンポーネントとは?
Figma内で再利用できる「部品テンプレート」です。
– 元(Main component)を変更すると、コピー(Instances)全てに反映される
– チームで一貫したデザインを維持できる
– 例えば「ボタン」「カード」「ヘッダー」などで使う

まずcomponentの作成

componentをフレームで利用する

テキスト内容は変更できる

### Variants
複数のデザインパターンを切り替えられるようにする
props = hover, default, … などと選択できるようにしていく

なるほど!なかなか奥が深い!

DifyにRedisを追加したい

$ sudo apt update
$ sudo apt install redis-server
$ sudo systemctl enable redis-server
$ sudo systemctl start redis-server

$ redis-cli ping

Vagrantfile

  config.vm.network "forwarded_port", guest: 6379, host: 6379 # 追加

うまくいかんな…