SONUB
JA English 한국어 日本語 中文 हिन्दी বাংলা اردو Español Português Français Deutsch Русский العربية Bahasa Indonesia Tiếng Việt ไทย Türkçe

通知を有効にする方法

このブラウザーまたはアドレスでは通知を利用できません。対応ブラウザーで安全な接続のSONUBサイトを開いてください。

通知がブロックされている場合は、ブラウザーの設定で許可してください。

  1. アドレスバー横のサイト設定、またはブラウザーのサイト設定を開き、このサイトの通知を許可に変更してください。
  2. 端末の設定でブラウザーの通知を許可し、おやすみモードや集中モードを確認してください。
  3. SONUBに戻ってページを再読み込みしてください。通知が有効になるとベルは表示されなくなります。

iPhoneやiPadではSONUBをホーム画面に追加し、そこから開いて通知を許可してください。

ログイン ログアウト 表示テーマ 標準テーマ ダークテーマ ピンクテーマ チャット 1:1 チャット グループチャット 掲示板 お知らせ ニュース掲示板 ご意見掲示板 自己紹介 メインメニュー

さあ、あなただけの人工知能を作ってみましょう。

次のを予測してを更新し、保存位置から続けます。train.py を実行し、前後のを比べましょう。

単語のブロックが一列に並び、最後に空きマスがある。その上に高さのちがう棒がついた候補ブロックが3つあり、そのうち1つが選ばれて列の最後につながり、矢印が最初に戻ってまた次を選ぶ。
6

学習とチェックポイント

[a,b]から[b,c]を予測します。は正解のので、真実性の保証ではありません。

区間選択→消去→・→→。毎更新後に・設定・辞書・回数・乱数を保存します。は同じ検証先頭区間だけの簡易診断です。

学習の目標 — 1マスずらした正解

資料をに変えると、長い番号の列になります。ここから13の区間を切り出し、前の12個を入力、1マスずらした後ろの12個を正解として使います。

  • トークンが [a, b, c, d] なら、入力 [a, b, c] の正解は [b, c, d] です。
  • 位置1は a だけを見て b を、位置2は a・b を見て c を、位置3は a・b・c を見て d を当てます。
  • 因果のおかげで、1回の forward で12個の問題を同時に解きます。

正解の印を人がつけなくても、文章そのものが問題と正解になります。だからインターネットの文章をそのまま学習資料に使えます(自己教師あり学習)。次のトークンをうまく当てるには文法・・形式・事実を知る必要があるので、この単純な目標が言語能力全体を引き出します。

損失 — 交差エントロピー(正解の確率の負の対数)

位置ごとに、が正解トークンに与えた確率を p とすると、その位置の損失は −log(p) です。区間の損失は12個の位置の損失の平均です。

  • p = 1 なら損失0: 正解を確信していました。
  • p = 0.5 なら 0.693、p = 0.1 なら 2.303、p = 0.01 なら 4.605: 正解に与えた確率が小さいほど、損失は急に大きくなります。
  • 不正解に大きな確率を与えても、直接の罰点は受けません。確率の合計が1なので、不正解が多くを取ると正解の p が減り、損失が大きくなります。

最初の損失は前もってわかります。学習前のモデルは語彙40個にほぼ同じ確率(1/40)を与えるので、損失は −log(1/40) = ln 40 ≈ 3.689 です。train.py が最初に表示する validation 3.7568 がこの値とほぼ同じなら、モデルは正しく初期化されています。語彙50,257個の なら、最初の損失は約10.8です。損失に exp をかけた値をパープレキシティ(perplexity)といい、exp(3.689) = 40 のように「いくつの中から迷っているか」と読めます。

語彙が数万個あるモデルの損失は、おおよそこう読みます。10以上ならランダムな水準、5〜7なら文法を、3〜5なら形式を、2〜3なら意味を学んでいる途中で、2より下がったら暗記()を疑います。語彙の数によって出発点が違うので、 では出発点 3.69 からどれだけ下がったかを見ます。

自分で確かめる lab/loss.py
import math

for p in [1.0, 0.9, 0.5, 0.1, 0.01]:
    print('p =', p, ' loss =', round(0.0 - math.log(p), 4))
for vocab in [40, 50257]:
    print('guess evenly among', vocab, '->', round(math.log(vocab), 4))
cd ~/xslm
mkdir -p lab
micro lab/loss.py
python3 -m lab.loss

実行結果

p = 1.0  loss = 0.0
p = 0.9  loss = 0.1054
p = 0.5  loss = 0.6931
p = 0.1  loss = 2.3026
p = 0.01  loss = 4.6052
guess evenly among 40 -> 3.6889
guess evenly among 50257 -> 10.8249

勾配降下法と学習率 — 勾配の反対方向へ少しずつ

勾配は「この数字を少し大きくすると、損失がどれだけ増えるか」です。損失を減らすには、勾配の反対方向に数字を動かします。

  • 新しい値 = 古い値 − × 勾配

学習率(learning_rate)は、一度にどれだけ動かすかです。下の実験は f(w) = (w − 3)² の最も低い所 w = 3 を、0から出発して20歩で探します。

  • 0.01: 慎重すぎて 1.0 付近までしか行けません。学習が遅いです。
  • 0.1: 2.97 まで来て、ほぼ到着しました。
  • 0.5: この関数では1歩でちょうど3に届きます。
  • 1.1: 大きく跳びすぎて谷の反対側へ越えることを繰り返し、−112 まで飛び出します(発散)。

実際のモデルは数字が数千〜数十億個あり、谷の形がわからないので、学習率は実験で決めます。XSLM の 0.03 は、小さなモデルが跳ねない慎重な値です。学習率を変えると settings.json が変わるので、新しいチェックポイントで始めます。

自分で確かめる lab/descent.py
# f(w) = (w - 3) ** 2 is smallest at w = 3. Its gradient is 2 * (w - 3).
for lr in [0.01, 0.1, 0.5, 1.1]:
    w = 0.0
    for step in range(20):
        w -= lr * 2 * (w - 3)
    print('lr', lr, '-> w =', round(w, 4))
cd ~/xslm
mkdir -p lab
micro lab/descent.py
python3 -m lab.descent

実行結果

lr 0.01 -> w = 0.9972
lr 0.1 -> w = 2.9654
lr 0.5 -> w = 3.0
lr 1.1 -> w = -112.0128

学習ループ1周 — 6つのステップ

train.py の1回は、この順序です。

  • 区間を選ぶ: 学習トークンの列からランダムな開始点を選び、13トークンを切り出します。
  • 勾配を消す: すべてのの grad を0にします。勾配は += で積み上がるので、前の繰り返しの値が残ると混ざります。今回の区間に出てこないトークンのの行はになく、backward が触れないので、ここで消さないと古い勾配がそのまま残ります。
  • 順伝播: model.loss が forward と損失の計算をします。
  • 逆伝播: loss.backward() で1,248個すべての勾配を求めます。
  • 更新: 勾配の大きさを制限してから、p.data −= 学習率 × 勾配 × 倍率 ですべての数字を動かします。
  • 保存・記録: チェックポイントを保存し、step と を表示します。

学習はこの1周を決めた回数だけ回ります。始める前と終わった後には、検証資料の最初の区間で損失を測り、学習に使わなかった文章でもよくなったかを見ます。

XSLM は、資料を何周したか()の代わりに歩数(step)で数えます。区間をランダムに引くので、ある部分は何度も、ある部分はあまり引かれません。大きなモデルもたいてい step で数えます。

勾配クリッピング — 一度に遠くへ行きすぎないように

ときどき、ある区間で勾配が急にとても大きくなります。そのまま更新すると、数字が一度に遠くへ跳び、これまでの学習を台なしにします(勾配爆発)。そこで train.py は更新の前に、勾配全体の長さを測ります。

  • 長さ = √(すべての勾配の2乗の合計)
  • 倍率 = min(1, 1 ÷ 長さ)
  • 長さが1以下なら倍率1でそのまま使い、1より大きければ長さがちょうど1になるよう、すべての勾配を同じ割合で縮めます。

方向はそのままで、大きさだけを縮めるのがポイントです。 では clip_grad_norm_ が同じことをします(ふつう最大の長さ 1.0)。分母の 1e-8 は、勾配がすべて0のときに0で割らないための小さな数です。

オプティマイザー — SGD と AdamW

勾配で数字を直す規則をといいます。

  • SGD(XSLM): 新しい値 = 古い値 − 学習率 × 勾配。今の勾配だけを見て、何も記憶しません。だからチェックポイントにオプティマイザーの状態を保存する必要がありません。
  • : 過去の勾配の流れ(速度)を記憶し、同じ方向ならより速く、揺れているならあまり動きません。
  • Adam・AdamW: 数字ごとに勾配の平均と大きさの平均を記憶し、数字ごとに合った歩幅を自分で決めます。AdamW は、数字が大きくなりすぎないよう少しずつ減らす重み減衰(weight decay)を加えます。大きなの学習の標準で、学習率はふつう 0.0003 くらいです。

Adam はパラメーターごとに数字を2つ多く記憶するので、学習を続けるにはチェックポイントにその状態(optimizer state)も保存する必要があります。だから PyTorch のチェックポイントには、ふつうモデルの重みと一緒にオプティマイザーの状態が入ります。学習中のも、重みだけを置くときの数倍かかります。

バッチ — 複数の区間の平均の勾配

XSLM は一度に区間1つ(バッチサイズ1)で勾配を求めます。区間ごとに内容が違うので勾配が大きく揺れ、損失も上がったり下がったりします。

大きなモデルは複数の区間を一度に計算し、平均の勾配を使います。例えば256トークンの区間16個(バッチサイズ16)なら、1回の更新で4,096トークンを見ます。

  • 平均をとると揺れが減り、更新の方向が安定します。
  • は複数の区間を同時に計算するので、バッチを大きくしても時間はほとんど増えません。
  • その代わり、その分のメモリーを使います。メモリーが足りなければ()、バッチや区間の長さを減らします。

メモリーが足りないときは、小さなバッチで何回も backward して勾配を集めてから1回更新する、勾配累積(gradient accumulation)を使います。XSLM の backward は始めるときにグラフ内の勾配を0に消すので、この方法をそのままでは使えません。

過学習と学習不足 — 学習の損失と検証の損失を一緒に見る

2つの損失を一緒に見ると、モデルの状態がわかります。

  • 両方下がる: よく学んでいます。
  • 学習の損失だけ下がり、検証の損失は上がる: 過学習です。学習資料を暗記して、初めて見る文章にはむしろ悪くなります。
  • 両方あまり下がらない: です。モデルが小さすぎるか、学習が足りないか、学習率が合っていません。

XSLM で実際に測ってみました(デスクトップの 、基本資料)。基本の学習率 0.03 で1,500回学習すると、検証の損失は 3.76 から 3.23 に下がりました。学習率 0.1 で3,000回学習すると、学習の損失は区間によって 1.3 まで下がりましたが、検証の損失は 4.42 に上がり、0.3 なら 5.28 まで上がりました。学習の文12組を暗記するのは簡単でも、初めて見る3組に通じる規則を学べるほど資料が多くないからです。

過学習を減らす最も確実な方法は資料を増やすことで、モデルを小さくする・・学習を早めに止める(検証の損失が最も低かったチェックポイントを使う)も使います。学習不足なら、モデルを大きくするか学習を増やします。

チェックポイント — 何をなぜ保存するのか

checkpoints/model.json は、学習を止めてから正確に続けるためのすべてを入れています。

  • weights: 9個のの数字1,248個。
  • step: これまでに行った更新の回数。
  • random: 乱数生成器の状態。続けて学習するとき、区間の順序を途切れずにつなげます。
  • settings・: モデルの大きさと番号の規則。generate.py と runner.py はこれだけを読んでモデルを作り直します。
  • data_hash: 学習・検証資料の の指紋。資料が変わったかどうかを見つけます。

だから3回ずつ2回に分けて学習した結果と、6回を一度に学習した結果は、数字1つまで同じです。自分で確かめてみてください。--steps 3 を2回実行した model.json を別の名前に移しておき、新しく --steps 6 を1回実行した結果と比べます。

保存は安全に行います。まず model.tmp にすべて書いてから、 で名前を変えます。名前の変更は一瞬で起きるので、書いている途中で を押したり電源が切れたりしても、model.json は前の完成版か新しい完成版のどちらかです。半分だけ書いた壊れたファイルは残りません。

PyTorch は torch.save で、重み・オプティマイザーの状態・step・設定をファイル(.pt)に保存します。XSLM は1歩ごとに保存しますが、大きなモデルは保存にも時間がかかるので、数百歩ごとに評価しながら保存します。

再現性 — seed で同じ結果をもう一度得る

コンピューターの乱数は、seed(種の番号)から決まった規則で作った数列です。seed が同じなら、いつも同じ数列が出ます。XSLM は seed 7 で2か所を決めます。

  • 重みの最初の値(model.py の random.Random(seed))。
  • 学習区間の順序(train.py の rng)。

だから同じ資料・同じ設定なら、誰がどこで実行しても同じ学習の過程をたどります。学習率1つだけを変えて比べるには、残りのランダム性が同じでなければならないので、は実験の基本です。seed を変えると違う最初の値から出発し、結果が変わります。運のよい seed 1つに頼った結論は信じにくいので、大事な比較は seed をいくつか変えて繰り返します。

ただし Python や OS の数学が違うと小数点の最後の桁が変わることがあり、とても長い学習では数字が少しずつずれることがあります。

学習の記録を読む — 数字が教えてくれること

train.py はこんな行を表示します。

  • parameters: 1248 resume: 0 — パラメーターの数と、続きから始めた歩数。初めてなら0です。
  • validation: 3.7568 — 学習前の検証の損失。(語彙の数)と近いはずです。
  • step: 1 loss: 3.6875 — 1歩ごとの、その区間の学習の損失。
  • validation: … — 学習後の検証の損失。
  • saved: checkpoints/model.json — 保存した場所。

1歩ごとの loss は区間が違うので上下します。数字1つではなく、何歩分もの流れを見ます。基本の20回なら 3.7 付近で少し動く程度で、数百〜数千回ならはっきり下がります。

=True は、出力をすぐに画面へ出せという意味です。これがないと Python が出力をためてから一度に見せるので、学習が止まったように見え、 でファイルに書くときも記録が遅れます。

学習がうまくいかないとき — 症状別の点検

症状から原因を絞ります。

  • 損失が 3.7 付近でほとんど変わらない: 学習回数が少ないです。--steps を増やすか、新しいチェックポイントで学習率を少し大きくしてみます。
  • 損失が大きく跳ねる、または大きくなるばかり: 学習率が大きすぎます。減らして新しく始めます。
  • : math domain error または nan: 確率が0になって log(0) を計算したか、数字が発散しました。学習率を下げ、勾配クリッピングが入っているかを見ます。
  • : changed settings/tokenizer または changed : チェックポイントと今の設定・資料が違います。古いチェックポイントを移して新しく始めます。
  • AssertionError: add more text: 学習か検証のトークンが13個より少ないです。raw.txt に行を足します。
  • 遅すぎる: width・context を減らすと速くなりますが、新しいチェックポイントが必要です。まず --steps を小さく分けて実行します。
  • 学習はできたのに の答えが変わらない: Runner は起動時に一度だけ読みます。止めて起動し直します。

入力して実験

train.pyを書き、短い学習の後さらに5回実行してresumeを見ます。速度は機器によります。

cd ~/xslm
micro train.py
python3 train.py --steps 20
python3 train.py --steps 5
ターミナルで実習する
train.py手入力する完全なソース

資料で変更を検出。とos.replaceで完全な保存を保ちます。SGDにモメンタムはありません。

import argparse
import hashlib
import json
import math
import os
import random
from pathlib import Path
from model import GPT
from tokenizer import BPE


def main():
    cfg = json.loads(Path('settings.json').read_text())
    parser = argparse.ArgumentParser()
    parser.add_argument('--steps', type=int, default=cfg['steps'])
    args = parser.parse_args()
    assert args.steps > 0, '--steps must be positive'
    token_state = json.loads(Path('data/tokenizer.json').read_text())
    tokenizer = BPE(**token_state)
    train_text = Path('data/train.txt').read_text(encoding='utf-8')
    val_text = Path('data/val.txt').read_text(encoding='utf-8')
    fingerprint = hashlib.sha256((train_text + '\0' + val_text).encode()).hexdigest()
    train, val = tokenizer.encode(train_text), tokenizer.encode(val_text)
    model = GPT(len(tokenizer.vocab), cfg['width'], cfg['context'], cfg['seed'])
    rng, step = random.Random(cfg['seed']), 0
    checkpoint = Path('checkpoints/model.json')
    if checkpoint.exists():
        saved = json.loads(checkpoint.read_text())
        assert saved['settings'] == cfg and saved['tokenizer'] == token_state, 'changed settings/tokenizer: start a new checkpoint'
        assert saved['data_hash'] == fingerprint, 'changed data: start a new checkpoint'
        model.load(saved['weights'])
        step = saved['step']
        rng.setstate((saved['random'][0], tuple(saved['random'][1]), saved['random'][2]))
    n = model.context
    assert len(train) > n and len(val) > n, 'add more text'
    print('parameters:', len(model.parameters), 'resume:', step, flush=True)
    print('validation:', round(model.loss(val[:n], val[1:n + 1]).data, 4), flush=True)
    checkpoint.parent.mkdir(exist_ok=True)
    for _ in range(args.steps):
        start = rng.randrange(len(train) - n)
        window = train[start:start + n + 1]
        for p in model.parameters:
            p.grad = 0
        loss = model.loss(window[:-1], window[1:])
        loss.backward()
        size = math.sqrt(sum(p.grad * p.grad for p in model.parameters))
        scale = min(1.0, 1.0 / (size + 1e-8))
        for p in model.parameters:
            p.data -= cfg['learning_rate'] * p.grad * scale
        step += 1
        state = {'settings': cfg, 'tokenizer': token_state, 'data_hash': fingerprint,
                 'weights': model.state(), 'step': step, 'random': rng.getstate()}
        temporary = checkpoint.with_suffix('.tmp')
        temporary.write_text(json.dumps(state, ensure_ascii=False), encoding='utf-8')
        os.replace(temporary, checkpoint)
        print('step:', step, 'loss:', round(loss.data, 4), flush=True)
    print('validation:', round(model.loss(val[:n], val[1:n + 1]).data, 4), flush=True)
    print('saved:', checkpoint, flush=True)


if __name__ == '__main__':
    main()

コードを1行ずつ読み解く

train.py 準備 — 設定・トークナイザー・資料

def main():
    cfg = json.loads(Path('settings.json').read_text())
    parser = argparse.ArgumentParser()
    parser.add_argument('--steps', type=int, default=cfg['steps'])
    args = parser.parse_args()
    assert args.steps > 0, '--steps must be positive'
    token_state = json.loads(Path('data/tokenizer.json').read_text())
    tokenizer = BPE(**token_state)
    train_text = Path('data/train.txt').read_text(encoding='utf-8')
    val_text = Path('data/val.txt').read_text(encoding='utf-8')
    fingerprint = hashlib.sha256((train_text + '\0' + val_text).encode()).hexdigest()
    train, val = tokenizer.encode(train_text), tokenizer.encode(val_text)
    model = GPT(len(tokenizer.vocab), cfg['width'], cfg['context'], cfg['seed'])

settings.json を読み、argparse で --steps オプションを受け取ります(なければ設定の steps)。tokenizer.json で を作り直し、学習・検証の文章を読みます。fingerprint は、2つの文章を \0 でつないで SHA-256 で作った指紋です。文章が1文字変わるだけで、まったく違う値になります。2つの文章をトークン番号のに変え、語彙の数に合わせて を作ります。

train.py 続きから学習する

    rng, step = random.Random(cfg['seed']), 0
    checkpoint = Path('checkpoints/model.json')
    if checkpoint.exists():
        saved = json.loads(checkpoint.read_text())
        assert saved['settings'] == cfg and saved['tokenizer'] == token_state, 'changed settings/tokenizer: start a new checkpoint'
        assert saved['data_hash'] == fingerprint, 'changed data: start a new checkpoint'
        model.load(saved['weights'])
        step = saved['step']
        rng.setstate((saved['random'][0], tuple(saved['random'][1]), saved['random'][2]))

rng は区間を選ぶ乱数生成器、step は更新の回数です。チェックポイントがあれば読み込み、設定・トークナイザー・資料の指紋が今と同じかを assert で確かめてから、重み・step・乱数の状態を復元します。 はタプルをリストで保存するので、setstate に渡す前に真ん中の部分を tuple に戻します。

train.py 始める前の確認

    n = model.context
    assert len(train) > n and len(val) > n, 'add more text'
    print('parameters:', len(model.parameters), 'resume:', step, flush=True)
    print('validation:', round(model.loss(val[:n], val[1:n + 1]).data, 4), flush=True)
    checkpoint.parent.mkdir(exist_ok=True)

n は文脈長(12)です。学習・検証のトークンが n より多くないと、n + 1 トークンの区間を切り出せません。パラメーターの数と続きから始めた歩数を表示し、学習前の検証の損失を測ります。val[:n] が入力、val[1:n + 1] が1マスずらした正解です。checkpoints フォルダーがなければ作ります。

train.py ランダムな区間の切り出し

    for _ in range(args.steps):
        start = rng.randrange(len(train) - n)
        window = train[start:start + n + 1]

randrange((train) − n) は、0以上(長さ − n)未満の開始点を引きます。その位置から n + 1 個を切り出して window にします。次の行で window[:-1] が入力12個、window[1:] が正解12個になります。

train.py 勾配の消去・順伝播・逆伝播

        for p in model.parameters:
            p.grad = 0
        loss = model.loss(window[:-1], window[1:])
        loss.backward()

すべてのパラメーターの grad を0にし、model.loss で損失を計算してから、backward で勾配を埋めます。backward はグラフ内のノードしか消さないので、今回の区間で使われなかったトークン埋め込みの行の古い勾配は、この繰り返し文が消します。

train.py 勾配クリッピングと SGD の更新

        size = math.sqrt(sum(p.grad * p.grad for p in model.parameters))
        scale = min(1.0, 1.0 / (size + 1e-8))
        for p in model.parameters:
            p.data -= cfg['learning_rate'] * p.grad * scale
        step += 1

size は1,248個の勾配を1つのとみなした長さです。scale は長さが1を超えれば 1/長さ、そうでなければ1です。すべてのパラメーターの data から 学習率 × 勾配 × scale を引きます。data を直接変えるので、この計算はグラフに記録されません。最後に step を1増やします。

train.py 安全に保存して記録する

        state = {'settings': cfg, 'tokenizer': token_state, 'data_hash': fingerprint,
                 'weights': model.state(), 'step': step, 'random': rng.getstate()}
        temporary = checkpoint.with_suffix('.tmp')
        temporary.write_text(json.dumps(state, ensure_ascii=False), encoding='utf-8')
        os.replace(temporary, checkpoint)
        print('step:', step, 'loss:', round(loss.data, 4), flush=True)

state に、続けて学習するのに必要なすべてを入れます。model.tmp にすべて書いてから、os.replace で model.json を一度に差し替えます。その次に、今回の1歩の損失を表示します。1歩ごとに保存するので、いつ止めても最後に終えた1歩までが残ります。

train.py 仕上げの検証

    print('validation:', round(model.loss(val[:n], val[1:n + 1]).data, 4), flush=True)
    print('saved:', checkpoint, flush=True)

学習を終えた重みで、始めたときと同じ検証区間の損失をもう一度測ります。始めたときの値と比べると、学習に使わなかった文章でもよくなったかがわかります。区間が1つだけなので、おおまかな合図としてだけ読みます。

理解を確認

model.jsonと継続回数を確認。異なる区間のlossは上下します。

困ったとき

Ctrl+Cで停止しても完了済み更新は残ります。設定や資料変更時は書き出した後、を別名にして再学習します。

よくある質問

loss が上がったり下がったりしますが、学習が壊れたのですか?

違います。1歩ごとに違う区間1つで損失を測るので、易しい区間なら低く、難しい区間なら高くなります。何歩分もの流れと、最初と最後の検証の損失を見てください。バッチを大きくすると揺れが減ります。

何回学習すればいいですか?

決まった答えはなく、検証の損失で決めます。検証の損失がもう下がらないか、上がり始めたら止めどきです。デスクトップで測ると、基本の学習率 0.03 で1,500回までは検証の損失が下がり、学習率 0.1 で3,000回ならむしろ上がりました。では1歩が遅いので、--steps 50 のように分けて実行しながら見守ってください。

GPU がなくても学習できますか?

できます。学習は足し算・掛け算の繰り返しにすぎないので、どんな でもできます。GPU は同じ計算を数千個同時にするので速いだけです。XSLM は仮想 CPU で、大きなモデルはふつう GPU(Apple の は )で、同じ原理の学習をします。

学習が終わったモデルは、使いながら自然にもっと学ぶのですか?

違います。学習は train.py を実行するときだけ起こります。Runner で答えている間は、重みを読むだけで変えません。会話で聞いたことを覚えているように見えるサービスは、会話の記録をにもう一度入れているのです。

ガイドライン

人工知能

エージェントツール

バイブコーディング

AI活用

AI資格

AI授業

IT基礎

チャット

掲示板

制作

コミュニティ

その他

用語辞典

知らない言葉を選んでみましょう。やさしい説明と関連用語が見られます。

その用語はまだありません。

用語を選ぶと説明が表示されます。