本ページでは、 自然言語処理 (NLP) を統合的に解説します。 トークン化・形態素解析・BoW/TF-IDF・Word2Vec・BERT・埋め込み・主要 NLP タスクを一気通貫で扱います。
日本語は分かち書きが必要・敬語や文脈依存が強い・複雑なエンコーディング等、 英語にはない難しさがあります。 本ページは日本語固有の課題も扱います。
論文記事から各用語のリンクをクリックすると、 該当箇所が開きます:
人間の言語をコンピュータで処理する分野。 困難要因:
テキストを処理単位(トークン)に分割。 多くの NLP の最初のステップ。
英語は空白で分かち書き → 日本語は文字列。 形態素解析器で分割:
1 2 3 4 | import fugashi tagger = fugashi.Tagger() for word in tagger("日本の首都は東京都です。"): print(word.surface, word.feature.pos1, word.feature.lemma) |
Transformer 系は語彙の固定化のためサブワード(語の一部)に分割:
1 2 3 4 | from transformers import AutoTokenizer tok = AutoTokenizer.from_pretrained('cl-tohoku/bert-base-japanese-v3') ids = tok.encode('東京は日本の首都です') print(tok.convert_ids_to_tokens(ids)) |
「は」「が」「の」など、 内容語でない単語を除く。 古典的 NLP の前処理。 BERT 系では通常不要。
古典的だが今も強力なベースライン特徴化手法。
文書 = 単語の出現数ベクトル。 順序情報は捨てる。
$$\mathrm{TF}\text{-}\mathrm{IDF}(t, d) = \mathrm{TF}(t, d) \cdot \log\frac{N}{\mathrm{DF}(t)}$$
1 2 3 4 5 6 7 | from sklearn.feature_extraction.text import TfidfVectorizer import fugashi tagger = fugashi.Tagger() def tokenize_ja(text): return [w.surface for w in tagger(text)] vec = TfidfVectorizer(tokenizer=tokenize_ja, token_pattern=None, max_features=5000) X = vec.fit_transform(documents) |
連続 n 単語を 1 特徴に。 文脈の一部を表現。 n=1(unigram)、 n=2(bigram)、 n=3(trigram)が標準。
「単語の意味は周囲に現れる単語で決まる」(分布仮説)に基づき、 文脈ウィンドウから単語を予測(Skip-gram)または逆(CBOW)。
1 2 3 4 5 | from gensim.models import Word2Vec sentences = [['東京', 'は', '日本', 'の', '首都'], ['大阪', 'は', '関西', 'の', '中心']] model = Word2Vec(sentences, vector_size=100, window=5, min_count=1, workers=4) print(model.wv['東京']) print(model.wv.most_similar('東京')) |
Stanford 2014。 単語共起行列の対数を直接フィット。 Word2Vec と同等の性能。
Facebook 2016。 サブワード単位の埋め込み。 未知語にも対応。
同じ単語でも文脈で異なるベクトル:「銀行に預金」と「川の銀行」の「銀行」は別ベクトル。
双方向 Transformer Encoder。 大規模コーパスで Masked LM 事前学習。 分類・抽出系タスクで圧倒的精度。
1 2 3 4 5 6 7 8 9 | from transformers import AutoTokenizer, AutoModel import torch tok = AutoTokenizer.from_pretrained('cl-tohoku/bert-base-japanese-v3') model = AutoModel.from_pretrained('cl-tohoku/bert-base-japanese-v3') ids = tok('東京は日本の首都です', return_tensors='pt') with torch.no_grad(): out = model(**ids) embedding = out.last_hidden_state.mean(dim=1) # 文ベクトル print(embedding.shape) |
文単位の意味類似度を測れるよう、 contrastive learning でファインチューン。 検索・クラスタリングに使う。
感情極性(positive/negative)、 カテゴリ、 トピック等。
1 2 3 | from transformers import pipeline clf = pipeline('sentiment-analysis', model='koheiduck/bert-japanese-finetuned-sentiment') print(clf('このサービスはとても便利です')) |
テキストから人名・地名・組織名等を抽出。 BIO タグ付け。
1 2 3 | from transformers import pipeline ner = pipeline('ner', model='Davlan/bert-base-multilingual-cased-ner-hrl') print(ner('オバマは2009年にアメリカ大統領になった')) |
文ベクトル間のコサイン類似度。 RAG・FAQ 検索の基盤。
Encoder-Decoder Transformer が標準。 NMT (Neural MT)。
1 2 | trans = pipeline('translation', model='Helsinki-NLP/opus-mt-ja-en') print(trans('東京は美しい都市です')) |
タスク指向 / オープンドメイン。 現代は ChatGPT 系 LLM が主流。
| 落とし穴 | 対処 |
|---|---|
| 日本語に英語向けトークナイザ | 日本語事前学習モデルとセットで使う。 fugashi/sudachi を活用。 |
| 前処理を過剰に | BERT 系は素のテキストで OK。 古典手法のときだけ前処理。 |
| 小データで巨大 LLM fine-tune | LoRA・プロンプト・Few-shot で対応。 |
| クラス不均衡 | Macro-F1 で評価、 weighted loss、 リサンプリング。 |
| 時系列リーク | 時系列分割。 訓練に未来データを混ぜない。 |
| BLEU だけで翻訳評価 | 最新は COMET・BERTScore。 人手評価も併用。 |
| LLM 出力をそのまま信用 | ハルシネーション対策に RAG・根拠検証。 |
SSDSE-B はテキストデータではないため、 wikipedia 日本語コーパス・口コミデータ・ニュース等を併用してください。
1 2 3 4 5 6 7 8 9 10 11 12 13 | from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.pipeline import Pipeline import fugashi tagger = fugashi.Tagger() def tok(text): return [w.surface for w in tagger(text)] pipe = Pipeline([ ('tfidf', TfidfVectorizer(tokenizer=tok, token_pattern=None)), ('lr', LogisticRegression(max_iter=1000)), ]) pipe.fit(texts_train, labels_train) print(pipe.score(texts_test, labels_test)) |
Hugging Face Trainer を使い、 bert-base-japanese-v3 を 3 エポック程度学習。 GPU が必要。
1 2 3 4 5 | from sentence_transformers import SentenceTransformer from sklearn.cluster import KMeans model = SentenceTransformer('intfloat/multilingual-e5-base') emb = model.encode(texts) clusters = KMeans(n_clusters=10, n_init=10).fit_predict(emb) |
「BERT で文書分類しました。」
「ネット口コミ 8,000 件(5 段階評価、 不均衡 [40%/25%/15%/12%/8%])を 3 クラス(高/中/低)に集約し、 cl-tohoku/bert-base-japanese-v3 を batch=16, lr=2e-5, 3epoch で fine-tune。 Stratified 5-fold CV で Macro-F1 = 0.78 ± 0.02。 ベースラインの TF-IDF+ロジスティック (Macro-F1 = 0.63) より大幅改善。 アテンション可視化で『否定形』『極性語』に強い注目を確認。 ハイパラ選定は内側 CV で実施した。」
| 用途 | パッケージ |
|---|---|
| 日本語形態素解析 | fugashi, mecab-python3, sudachipy, janome |
| 解析パイプ | spacy, ginza, stanza |
| 古典NLP | sklearn.feature_extraction.text, nltk |
| 単語埋め込み | gensim, fasttext |
| 事前学習モデル | transformers, sentence-transformers |
| トークナイザ | tokenizers, sentencepiece, tiktoken |
| トピック | gensim, bertopic, top2vec |
| ベクトル検索 | faiss, chromadb, qdrant, weaviate |
| LLMフレーム | langchain, llamaindex, dspy |
| 評価 | seqeval, sacrebleu, evaluate (HuggingFace) |
日本語は文脈で省略される情報が多い。 BERT 系は文脈学習で対応するが、 完璧ではない。 敬語レベル(タメ口・丁寧語・尊敬語・謙譲語)は文体識別の特徴。
漢字・ひらがな・カタカナ・英数字が同一文に混在。 サブワード分割が破綻しやすい。 SentencePiece が標準的に使われる理由の一つ。
古典文書・Web テキストではメタ情報除去が必要。
A. ノー。 (1) 小規模・高速・確定的処理ではルール / 線形が依然有用、 (2) LLM の前処理として古典手法が使われる、 (3) コスト・遅延・プライバシー要件で LLM が使えない場面が多い。
A. 2026 時点では cl-tohoku/bert-base-japanese-v3、 LINE LDLM、 rinna が主要。 ドメイン特化なら継続事前学習を検討。
A. 知識更新性が高い・出典が必要 → RAG。 文体・スタイル変更 → Fine-tune。 多くは両者の併用。
A. 完全に防ぐのは困難。 (1) RAG で根拠付け、 (2) Self-Consistency や別モデルでの検証、 (3) 出力の確信度を測定、 (4) 重要な決定では人間がレビュー。
$$\mathrm{BLEU} = \mathrm{BP} \cdot \exp\left(\sum_{n=1}^N w_n \log p_n\right)$$
$p_n$ は n-gram 精度、 $\mathrm{BP}$ は短さペナルティ。 0-1 で大きいほど良い。 単一文では信頼性が低い。
BERT 埋め込みのコサイン類似度ベース。 BLEU/ROUGE より人手評価との相関が高い。
$$\mathrm{BERTScore} = \frac{1}{|x|}\sum_{x_i} \max_{\hat{x}_j} \cos(\mathbf{e}_{x_i}, \mathbf{e}_{\hat{x}_j})$$
$$\mathrm{PPL} = \exp\left(-\frac{1}{N}\sum_i \log p(x_i | x_{
「次の単語をどれくらい予測しにくいか」。 小さいほど良い言語モデル。
エンティティ単位の P/R/F1(部分一致でなく完全一致)。 seqeval ライブラリが標準。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | from sentence_transformers import SentenceTransformer import faiss import numpy as np # 1. インデックス構築 embedder = SentenceTransformer('intfloat/multilingual-e5-base') docs = ['文書1...', '文書2...', ...] embeddings = embedder.encode([f'passage: {d}' for d in docs]) index = faiss.IndexFlatIP(embeddings.shape[1]) faiss.normalize_L2(embeddings) index.add(embeddings) # 2. クエリ → 検索 query = '東京の人口は?' q_emb = embedder.encode([f'query: {query}']) faiss.normalize_L2(q_emb) scores, idx = index.search(q_emb, k=5) retrieved = [docs[i] for i in idx[0]] # 3. LLM に注入 prompt = f"""以下の文書を参考に質問に答えてください。 文書: {chr(10).join(retrieved)} 質問: {query} 回答:""" |
主要モデル・タスク・指標・前処理用語を一覧で。 各チップから関連セクションへジャンプできます。
SSDSE-B-2026 自体は数値データですが、 都道府県名と都道府県の特徴量を「擬似的なテキストドキュメント」として扱う実例を示します(教育目的)。
各都道府県の県庁所在地名や地理キーワードを文字列として連結し、 TF-IDF 行列を作成して類似都道府県を求めます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | import pandas as pd from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics.pairwise import cosine_similarity df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', header=1) # 各都道府県の特徴を「テキスト化」(数値→文字列の擬似ドキュメント) docs = [] for _, row in df.iterrows(): pref = str(row['都道府県']) # 各列を「項目名_値帯」の形式に変換(例:'人口_多', '高齢化率_高') tokens = [pref] docs.append(' '.join(tokens)) # 実テキスト例:県庁所在地と地域情報 labels = df['都道府県'].tolist() texts = [f"{name} 県庁 地方 統計 経済 人口" for name in labels] vectorizer = TfidfVectorizer(analyzer='char_wb', ngram_range=(2,3)) X = vectorizer.fit_transform(texts) print('TF-IDF 行列の形:', X.shape) # コサイン類似度で似た県を探す sim = cosine_similarity(X) print(f'東京と大阪の類似度: {sim[12, 26]:.3f}') |
n-gram は文字列分類のベースライン。 47都道府県名を文字バイグラム化すれば、 似た音韻を持つ県名がベクトル空間で近くなります。
1 2 3 4 5 6 7 | from sklearn.feature_extraction.text import CountVectorizer prefs = df['都道府県'].tolist() # 47県名 cv = CountVectorizer(analyzer='char', ngram_range=(2,2)) bigram_mat = cv.fit_transform(prefs) print('バイグラム種類数:', len(cv.vocabulary_)) print('「岡」を含む県:', [p for p in prefs if '岡' in p]) |
SSDSE 単体ではテキスト分析の練習が難しいため、 以下のような外部公的テキストデータと組み合わせることを推奨:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.pipeline import Pipeline from sklearn.model_selection import cross_val_score texts = ['東京は首都です', '大阪は商業の街', '京都は古い都', '横浜は港町'] labels = [1, 0, 1, 0] pipe = Pipeline([ ('tfidf', TfidfVectorizer(analyzer='char_wb', ngram_range=(2,3))), ('clf', LogisticRegression(max_iter=1000)) ]) scores = cross_val_score(pipe, texts, labels, cv=2) print(f'CV 平均: {scores.mean():.3f}') |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | from scipy.spatial.distance import cosine, cdist from scipy import sparse import numpy as np # 文書ベクトル(疎ベクトル) v1 = np.array([1, 2, 0, 3, 0]) v2 = np.array([0, 1, 2, 1, 1]) # scipy.spatial.distance.cosine は距離(1-類似度) dist = cosine(v1, v2) print(f'コサイン類似度: {1 - dist:.3f}') # 多文書一括の類似度行列 docs = np.random.RandomState(0).rand(10, 50) # 例示用(実データに置換) sim_matrix = 1 - cdist(docs, docs, metric='cosine') |
1 2 3 4 5 6 7 8 9 10 11 12 13 | from transformers import AutoTokenizer, AutoModel import torch tokenizer = AutoTokenizer.from_pretrained('cl-tohoku/bert-base-japanese-v3') model = AutoModel.from_pretrained('cl-tohoku/bert-base-japanese-v3') texts = ['東京は日本の首都です', '大阪は商業の中心地'] inputs = tokenizer(texts, padding=True, return_tensors='pt') with torch.no_grad(): outputs = model(**inputs) # [CLS] トークンの出力を文ベクトルとして使う sent_vec = outputs.last_hidden_state[:, 0, :] print('文ベクトル形:', sent_vec.shape) |
1 2 3 4 5 6 7 8 | from sentence_transformers import SentenceTransformer from sklearn.metrics.pairwise import cosine_similarity model = SentenceTransformer('intfloat/multilingual-e5-small') sentences = ['今日は天気が良い', '本日は晴天です', '株価が下落した'] embs = model.encode(sentences) sim = cosine_similarity(embs) print(sim) |
1 2 3 4 5 6 7 8 9 10 11 12 13 | from gensim.models import Word2Vec, LdaModel from gensim.corpora import Dictionary # tokens は分かち書き済みの list of list tokens = [['東京', 'は', '日本', 'の', '首都'], ['大阪', 'は', '商業', 'の', '街']] w2v = Word2Vec(tokens, vector_size=50, min_count=1, window=3, epochs=10) print('東京の近傍:', w2v.wv.most_similar('東京', topn=3)) dic = Dictionary(tokens) corpus = [dic.doc2bow(t) for t in tokens] lda = LdaModel(corpus, num_topics=2, id2word=dic, passes=5) for tid, w in lda.print_topics(): print(tid, w) |
1 2 3 4 5 6 7 8 9 10 11 12 | import spacy nlp = spacy.load('ja_ginza') doc = nlp('東京は日本の首都です。大阪は商業の中心地です。') for sent in doc.sents: for tok in sent: print(tok.text, tok.lemma_, tok.pos_, tok.dep_) # 固有表現抽出 for ent in doc.ents: print(ent.text, ent.label_) |