Skip to content

필사 모드: NLP 与文本处理完全指南:从 BERT 微调到 RAG 系统、多语言处理

中文
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.

NLP 与文本处理完全指南:从 BERT 微调到 RAG 系统、多语言处理

自然语言处理(NLP)是让计算机理解和生成人类语言的核心 AI 技术。本指南从文本预处理的基础出发,系统覆盖 BERT 微调、RAG 流水线构建、多语言处理等内容,并配有实战代码。

目录

  1. 文本预处理与分词
  2. 子词分词:BPE、WordPiece、SentencePiece
  3. 词嵌入:Word2Vec、FastText、GloVe
  4. 序列模型的演进:从 RNN 到 Transformer
  5. BERT 系列模型与微调
  6. 文本任务实战
  7. 构建 RAG 系统
  8. 多语言 NLP 与韩语的特殊性
  9. 小测验

1. 文本预处理与分词

1.1 韩语形态素分析(KoNLPy、MeCab)

韩语是一种黏着语(agglutinative language),词干与各种词缀结合来表达含义。因此英语那种基于空格的简单分词方式对韩语并不适用。KoNLPy 是韩语形态素分析的代表性 Python 库。

from konlpy.tag import Okt, Mecab, Komoran

okt = Okt()
text = "자연어 처리는 인공지능의 핵심 분야입니다."

# 形态素分析
morphs = okt.morphs(text)
print("形态素:", morphs)
# ['자연어', '처리', '는', '인공지능', '의', '핵심', '분야', '입니다', '.']

# 词性标注
pos_tags = okt.pos(text)
print("词性标注:", pos_tags)
# [('자연어', 'Noun'), ('처리', 'Noun'), ('는', 'Josa'), ...]

# 名词提取
nouns = okt.nouns(text)
print("名词:", nouns)
# ['자연어', '처리', '인공지능', '핵심', '분야']

# 使用 MeCab(更快、更准确的分析)
# mecab = Mecab()
# mecab_morphs = mecab.morphs(text)

MeCab 是将日语形态素分析器移植到韩语环境的工具,在速度和准确度方面表现出色。处理大规模数据时推荐使用 MeCab。

# 使用 KoNLPy 的韩语文本预处理流水线
import re
from konlpy.tag import Okt

def preprocess_korean(text: str) -> list[str]:
    """韩语文本预处理流水线"""
    # 去除特殊字符(仅保留韩文、英文、数字)
    text = re.sub(r'[^가-힣a-zA-Z0-9\s]', '', text)
    # 去除多余空白
    text = re.sub(r'\s+', ' ', text).strip()

    okt = Okt()
    # 仅提取名词、动词、形容词
    tokens = [
        word for word, pos in okt.pos(text)
        if pos in ['Noun', 'Verb', 'Adjective'] and len(word) > 1
    ]
    return tokens

sample = "자연어 처리 기술이 빠르게 발전하고 있습니다!"
print(preprocess_korean(sample))

1.2 停用词处理与文本归一化

# 韩语停用词处理
KOREAN_STOPWORDS = [
    '이', '가', '을', '를', '은', '는', '의', '에', '에서',
    '로', '으로', '와', '과', '도', '만', '까지', '부터'
]

def remove_stopwords(tokens: list[str], stopwords: list[str]) -> list[str]:
    return [token for token in tokens if token not in stopwords]

# 文本归一化
def normalize_text(text: str) -> str:
    # 去除重复字符: 'ㅋㅋㅋㅋ' -> 'ㅋㅋ'
    text = re.sub(r'(.)\1{2,}', r'\1\1', text)
    # 转换为英文小写
    text = text.lower()
    return text

2. 子词分词:BPE、WordPiece、SentencePiece

2.1 BPE(字节对编码)

BPE 通过反复合并频繁出现的字符对来构建词表,能有效解决 OOV(Out-of-Vocabulary,词表外词)问题,被 GPT 系列模型采用。

from tokenizers import Tokenizer, trainers, pre_tokenizers, models, decoders

# 训练 BPE 分词器
tokenizer = Tokenizer(models.BPE())
tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=True)
tokenizer.decoder = decoders.ByteLevel()

trainer = trainers.BpeTrainer(
    vocab_size=30000,
    min_frequency=2,
    special_tokens=["[UNK]", "[PAD]", "[BOS]", "[EOS]"]
)

# 训练数据文件路径列表
files = ["train_corpus.txt"]
tokenizer.train(files, trainer)

# 分词示例
encoding = tokenizer.encode("자연어 처리는 fascinating합니다!")
print("Token:", encoding.tokens)
print("ID:", encoding.ids)

# 保存与加载
tokenizer.save("bpe_tokenizer.json")
loaded_tokenizer = Tokenizer.from_file("bpe_tokenizer.json")

2.2 WordPiece 与 SentencePiece

WordPiece 是 BERT 使用的子词分词方式,通过给子词加上 ## 前缀来表示连续性。SentencePiece 则是一种与语言无关的方式,无需按语言边界即可处理原始文本。

import sentencepiece as spm

# 训练 SentencePiece 模型
spm.SentencePieceTrainer.train(
    input='corpus.txt',
    model_prefix='sp_model',
    vocab_size=32000,
    character_coverage=0.9995,  # 韩语/日语建议设置较高的值
    model_type='bpe',            # 或 'unigram'
    pad_id=0,
    unk_id=1,
    bos_id=2,
    eos_id=3
)

sp = spm.SentencePieceProcessor()
sp.load('sp_model.model')

text = "자연어 처리는 NLP의 핵심입니다."
tokens = sp.encode_as_pieces(text)
ids = sp.encode_as_ids(text)
print("SentencePiece Token:", tokens)
# ['▁자연어', '▁처리', '는', '▁NLP', '의', '▁핵심', '입니다', '.']
方式使用模型特点
BPEGPT-2、RoBERTa基于频率合并,结果确定
WordPieceBERT、DistilBERT似然最大化,## 前缀
SentencePieceT5、mBART、XLM-R语言无关,处理空白字符
Unigram LMAlbert、mBERT基于概率模型,切分方式灵活

3. 词嵌入:Word2Vec、FastText、GloVe

3.1 Word2Vec

Word2Vec 是一种基于神经网络的词嵌入方法,包含 CBOW(Continuous Bag of Words)和 Skip-gram 两种架构。

from gensim.models import Word2Vec
from konlpy.tag import Okt

# 准备韩语语料
okt = Okt()
corpus = [
    "자연어 처리는 인공지능의 핵심 분야입니다",
    "BERT는 양방향 트랜스포머 모델입니다",
    "텍스트 분류와 감성 분석에 BERT를 활용합니다",
]

# 形态素分析后进行分词
tokenized_corpus = [okt.morphs(sentence) for sentence in corpus]

# 训练 Word2Vec(Skip-gram)
model = Word2Vec(
    sentences=tokenized_corpus,
    vector_size=100,    # 嵌入维度
    window=5,           # 上下文窗口大小
    min_count=1,
    sg=1,               # 1: Skip-gram, 0: CBOW
    workers=4,
    epochs=100
)

# 查找相似词
similar_words = model.wv.most_similar('자연어', topn=5)
print("相似词:", similar_words)

# 提取词向量
vector = model.wv['자연어']
print("向量 shape:", vector.shape)  # (100,)

# 词类比(king - man + woman ≈ queen)
result = model.wv.most_similar(
    positive=['왕', '여자'],
    negative=['남자'],
    topn=3
)

3.2 FastText 与 GloVe

FastText 将词表示为字符 n-gram 之和,因而也能处理 OOV 词。GloVe 则是利用全局共现矩阵的一种嵌入方法。

from gensim.models import FastText
import numpy as np

# 训练 FastText
ft_model = FastText(
    sentences=tokenized_corpus,
    vector_size=100,
    window=5,
    min_count=1,
    sg=1,
    min_n=2,   # 最小 n-gram 长度
    max_n=6,   # 最大 n-gram 长度
    epochs=100
)

# 也能处理 OOV 词
# 训练数据中没有的词也可以通过 n-gram 生成向量
oov_vector = ft_model.wv['자연언어처리']  # 即使训练时未出现过也能处理

# 加载预训练的韩语 FastText 嵌入
from gensim.models import KeyedVectors
# cc.ko.300.vec: 基于 Common Crawl 的韩语 FastText(由 fasttext.cc 提供)
# ko_fasttext = KeyedVectors.load_word2vec_format('cc.ko.300.vec')

3.3 使用 Sentence Transformers 的句子嵌入

这是一种克服词级嵌入局限、以句子为单位进行嵌入的方法。

from sentence_transformers import SentenceTransformer
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity

# 加载支持多语言的模型
model = SentenceTransformer('paraphrase-multilingual-mpnet-base-v2')
# 韩语专用模型
ko_model = SentenceTransformer('snunlp/KR-ELECTRA-discriminator')

sentences = [
    "자연어 처리는 인공지능의 한 분야입니다.",
    "NLP는 AI의 중요한 영역입니다.",
    "오늘 날씨가 맑고 좋네요.",
]

# 生成句子嵌入
embeddings = model.encode(sentences)
print("嵌入 shape:", embeddings.shape)  # (3, 768)

# 计算余弦相似度
sim_matrix = cosine_similarity(embeddings)
print("相似度矩阵:")
print(sim_matrix)

# 语义搜索
query = "언어 모델이란 무엇인가?"
query_embedding = model.encode([query])
similarities = cosine_similarity(query_embedding, embeddings)[0]

for sent, score in zip(sentences, similarities):
    print(f"相似度 {score:.4f}: {sent}")

4. 序列模型的演进:从 RNN 到 Transformer

4.1 从 RNN 到 LSTM、GRU 的演进

import torch
import torch.nn as nn

# 基于 LSTM 的文本分类器
class LSTMClassifier(nn.Module):
    def __init__(self, vocab_size, embed_dim, hidden_dim, num_classes):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
        self.lstm = nn.LSTM(
            embed_dim, hidden_dim,
            num_layers=2,
            batch_first=True,
            dropout=0.3,
            bidirectional=True
        )
        self.classifier = nn.Sequential(
            nn.Linear(hidden_dim * 2, hidden_dim),
            nn.ReLU(),
            nn.Dropout(0.3),
            nn.Linear(hidden_dim, num_classes)
        )

    def forward(self, x):
        embedded = self.embedding(x)           # (batch, seq_len, embed_dim)
        output, (hidden, cell) = self.lstm(embedded)
        # 拼接双向的最后一个 hidden state
        final_hidden = torch.cat([hidden[-2], hidden[-1]], dim=1)
        return self.classifier(final_hidden)

# GRU 参数比 LSTM 少,训练更快
class GRUClassifier(nn.Module):
    def __init__(self, vocab_size, embed_dim, hidden_dim, num_classes):
        super().__init__()
        self.embedding = nn.Embedding(vocab_size, embed_dim, padding_idx=0)
        self.gru = nn.GRU(
            embed_dim, hidden_dim,
            num_layers=2,
            batch_first=True,
            bidirectional=True
        )
        self.fc = nn.Linear(hidden_dim * 2, num_classes)

    def forward(self, x):
        embedded = self.embedding(x)
        _, hidden = self.gru(embedded)
        out = torch.cat([hidden[-2], hidden[-1]], dim=1)
        return self.fc(out)

4.2 Attention 机制

class MultiHeadAttention(nn.Module):
    def __init__(self, d_model, num_heads):
        super().__init__()
        self.d_model = d_model
        self.num_heads = num_heads
        self.d_k = d_model // num_heads

        self.W_q = nn.Linear(d_model, d_model)
        self.W_k = nn.Linear(d_model, d_model)
        self.W_v = nn.Linear(d_model, d_model)
        self.W_o = nn.Linear(d_model, d_model)

    def scaled_dot_product_attention(self, Q, K, V, mask=None):
        scores = torch.matmul(Q, K.transpose(-2, -1)) / (self.d_k ** 0.5)
        if mask is not None:
            scores = scores.masked_fill(mask == 0, -1e9)
        attn_weights = torch.softmax(scores, dim=-1)
        return torch.matmul(attn_weights, V), attn_weights

    def forward(self, query, key, value, mask=None):
        batch_size = query.size(0)
        Q = self.W_q(query).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
        K = self.W_k(key).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
        V = self.W_v(value).view(batch_size, -1, self.num_heads, self.d_k).transpose(1, 2)
        attn_output, _ = self.scaled_dot_product_attention(Q, K, V, mask)
        attn_output = attn_output.transpose(1, 2).contiguous().view(batch_size, -1, self.d_model)
        return self.W_o(attn_output)

5. BERT 系列模型与微调

5.1 BERT 的预训练任务

BERT(Bidirectional Encoder Representations from Transformers)使用两种预训练任务。

  • MLM(Masked Language Model,掩码语言模型):将输入 token 中的 15% 替换为 [MASK],并预测原始 token
  • NSP(Next Sentence Prediction,下一句预测):对两个句子是否实际连续进行二分类

5.2 使用 KLUE-BERT 进行韩语文本分类

from transformers import AutoTokenizer, AutoModelForSequenceClassification
from transformers import TrainingArguments, Trainer
from datasets import Dataset
import torch
import numpy as np
from sklearn.metrics import accuracy_score, f1_score

# 加载 KLUE-BERT
model_name = "klue/bert-base"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForSequenceClassification.from_pretrained(
    model_name,
    num_labels=5  # 情感分类: 非常负面/负面/中立/正面/非常正面
)

# 准备数据
train_data = {
    "text": [
        "이 영화 정말 재미있어요! 강력 추천합니다.",
        "서비스가 너무 불친절하고 품질도 별로예요.",
        "평범한 수준이네요. 특별한 점은 없었어요.",
    ],
    "label": [4, 0, 2]
}

def tokenize_function(examples):
    return tokenizer(
        examples["text"],
        max_length=128,
        padding="max_length",
        truncation=True
    )

dataset = Dataset.from_dict(train_data)
tokenized_dataset = dataset.map(tokenize_function, batched=True)

# 训练配置
training_args = TrainingArguments(
    output_dir="./klue-bert-sentiment",
    num_train_epochs=3,
    per_device_train_batch_size=16,
    per_device_eval_batch_size=32,
    warmup_ratio=0.1,
    weight_decay=0.01,
    learning_rate=2e-5,
    logging_dir="./logs",
    evaluation_strategy="epoch",
    save_strategy="epoch",
    load_best_model_at_end=True,
    metric_for_best_model="f1",
    fp16=True,  # 节省 GPU 显存
)

def compute_metrics(eval_pred):
    logits, labels = eval_pred
    predictions = np.argmax(logits, axis=-1)
    return {
        "accuracy": accuracy_score(labels, predictions),
        "f1": f1_score(labels, predictions, average="weighted")
    }

trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_dataset,
    eval_dataset=tokenized_dataset,
    compute_metrics=compute_metrics,
)
trainer.train()

5.3 KoELECTRA NER 微调

from transformers import AutoTokenizer, AutoModelForTokenClassification
from transformers import pipeline

# 基于 KoELECTRA 的 NER
model_name = "monologg/koelectra-base-finetuned-ner"
tokenizer = AutoTokenizer.from_pretrained(model_name)
ner_model = AutoModelForTokenClassification.from_pretrained(model_name)

# NER 流水线
ner_pipeline = pipeline(
    "ner",
    model=ner_model,
    tokenizer=tokenizer,
    aggregation_strategy="simple"
)

text = "삼성전자 이재용 회장이 서울 강남구에서 기자회견을 열었습니다."
entities = ner_pipeline(text)

for entity in entities:
    print(f"实体: {entity['word']}, 类型: {entity['entity_group']}, "
          f"得分: {entity['score']:.4f}")
# 实体: 삼성전자, 类型: ORG, 得分: 0.9987
# 实体: 이재용, 类型: PER, 得分: 0.9923
# 实体: 서울 강남구, 类型: LOC, 得分: 0.9856

5.4 RoBERTa、DeBERTa 对比

模型主要改进点特点
BERT基准模型MLM + NSP
RoBERTa移除 NSP,使用更多数据动态掩码,更大批次
DeBERTa分离注意力机制位置与内容分开处理
KLUE-BERT韩语专用使用韩语维基百科/新闻训练
KoELECTRAELECTRA 方式Generator-Discriminator 结构

6. 文本任务实战

6.1 文本分类与情感分析

# 用于各种 NLP 任务的 HuggingFace 流水线
from transformers import pipeline

# 情感分析
sentiment = pipeline(
    "sentiment-analysis",
    model="snunlp/KR-FinBert-SC"
)
result = sentiment("이 제품은 가격 대비 품질이 매우 훌륭합니다.")
print(result)  # [{'label': 'positive', 'score': 0.9876}]

# 文本摘要
summarizer = pipeline(
    "summarization",
    model="gogamza/kobart-summarization"
)
long_text = """
자연어 처리(NLP)는 컴퓨터가 인간의 언어를 이해하고 처리할 수 있도록 하는
인공지능의 한 분야입니다. 최근 트랜스포머 기반 모델의 등장으로 NLP 분야는
급격한 발전을 이루었습니다. BERT, GPT, T5 등의 모델은 다양한 NLP 태스크에서
인간 수준에 근접한 성능을 보여주고 있습니다.
"""
summary = summarizer(long_text, max_length=50, min_length=20)
print(summary[0]['summary_text'])

# 问答(QA)
qa_pipeline = pipeline(
    "question-answering",
    model="monologg/koelectra-base-v3-finetuned-korquad"
)
context = "서울은 대한민국의 수도이며 인구 약 950만 명이 거주하는 대도시입니다."
question = "서울의 인구는 얼마입니까?"
answer = qa_pipeline(question=question, context=context)
print(f"答案: {answer['answer']}, 得分: {answer['score']:.4f}")

6.2 机器翻译(Neural Machine Translation)

from transformers import MarianMTModel, MarianTokenizer

# 基于 Helsinki-NLP 的翻译模型
model_name = "Helsinki-NLP/opus-mt-ko-en"
tokenizer = MarianTokenizer.from_pretrained(model_name)
model = MarianMTModel.from_pretrained(model_name)

def translate(text: str, src_lang: str = "ko", tgt_lang: str = "en") -> str:
    inputs = tokenizer(text, return_tensors="pt", padding=True)
    translated = model.generate(**inputs, num_beams=5)
    return tokenizer.decode(translated[0], skip_special_tokens=True)

korean_text = "인공지능 기술이 우리의 삶을 크게 변화시키고 있습니다."
english_translation = translate(korean_text)
print(f"翻译结果: {english_translation}")

7. 构建 RAG 系统

7.1 RAG 流水线概览

RAG(Retrieval-Augmented Generation,检索增强生成)是一种基于检索、利用外部知识来提升 LLM 回答质量的技术。大致由文档分块 → 嵌入 → 向量存储 → 检索 → 重排序 → 生成等阶段构成。

7.2 使用 LangChain 构建 RAG 流水线

from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import PGVector
from langchain_community.chat_models import ChatOllama
from langchain.chains import RetrievalQA
from langchain.schema import Document

# 第一步: 文档分块
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=512,
    chunk_overlap=50,
    separators=["\n\n", "\n", "。", ".", " ", ""],
    length_function=len,
)

documents = [
    Document(
        page_content="BERT는 2018년 Google이 발표한 양방향 트랜스포머 모델입니다...",
        metadata={"source": "nlp_guide.txt", "page": 1}
    )
]
chunks = text_splitter.split_documents(documents)
print(f"分块数量: {len(chunks)}")

# 第二步: 设置嵌入模型(支持韩语)
embeddings = HuggingFaceEmbeddings(
    model_name="jhgan/ko-sroberta-multitask",
    model_kwargs={"device": "cuda"},
    encode_kwargs={"normalize_embeddings": True}
)

# 第三步: 存入 pgvector
CONNECTION_STRING = "postgresql+psycopg2://user:password@localhost:5432/ragdb"
COLLECTION_NAME = "nlp_documents"

vectorstore = PGVector.from_documents(
    documents=chunks,
    embedding=embeddings,
    collection_name=COLLECTION_NAME,
    connection_string=CONNECTION_STRING,
)

# 第四步: 配置检索器
retriever = vectorstore.as_retriever(
    search_type="similarity",
    search_kwargs={"k": 5}
)

# 第五步: 构建 RAG 链
llm = ChatOllama(model="llama3.1:8b-instruct-q4_K_M")
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=retriever,
    return_source_documents=True,
    verbose=True,
)

query = "BERT의 사전 학습 방법은 무엇인가요?"
result = qa_chain.invoke({"query": query})
print(result["result"])

7.3 两阶段检索:Bi-encoder + Cross-encoder

from sentence_transformers import SentenceTransformer, CrossEncoder
import numpy as np

# 阶段 1: 用 Bi-encoder 检索候选文档(速度快)
bi_encoder = SentenceTransformer('jhgan/ko-sroberta-multitask')

# 阶段 2: 用 Cross-encoder 重排序(精度高)
cross_encoder = CrossEncoder('cross-encoder/mmarco-mMiniLMv2-L12-H384-v1')

def two_stage_retrieval(query: str, corpus: list[str], top_k: int = 5) -> list[str]:
    """Bi-encoder + Cross-encoder 两阶段检索"""
    # 阶段 1: 快速初步检索(取前 20 个)
    query_emb = bi_encoder.encode(query, convert_to_tensor=True)
    corpus_emb = bi_encoder.encode(corpus, convert_to_tensor=True)

    from sentence_transformers.util import cos_sim
    scores = cos_sim(query_emb, corpus_emb)[0]
    top_indices = scores.argsort(descending=True)[:20].tolist()
    candidates = [corpus[i] for i in top_indices]

    # 阶段 2: 用 Cross-encoder 精细重排序
    pairs = [[query, doc] for doc in candidates]
    rerank_scores = cross_encoder.predict(pairs)

    # 返回最终的 top_k
    ranked = sorted(
        zip(candidates, rerank_scores),
        key=lambda x: x[1],
        reverse=True
    )
    return [doc for doc, _ in ranked[:top_k]]

# 使用示例
corpus = [
    "BERT는 마스크 언어 모델로 사전 학습됩니다.",
    "GPT는 자동회귀 방식으로 텍스트를 생성합니다.",
    "오늘 날씨는 맑고 기온은 20도입니다.",
]
results = two_stage_retrieval("BERT 학습 방법", corpus)
for doc in results:
    print(f"- {doc}")

8. 多语言 NLP 与韩语的特殊性

8.1 mBERT 与 XLM-R

from transformers import AutoTokenizer, AutoModel
import torch

# XLM-RoBERTa: 支持 100 种语言
xlmr_tokenizer = AutoTokenizer.from_pretrained("xlm-roberta-base")
xlmr_model = AutoModel.from_pretrained("xlm-roberta-base")

# 处理多语言句子
sentences = {
    "ko": "자연어 처리는 인공지능의 핵심입니다.",
    "en": "NLP is the core of artificial intelligence.",
    "ja": "自然言語処理はAIの核心です。",
}

for lang, sentence in sentences.items():
    inputs = xlmr_tokenizer(
        sentence,
        return_tensors="pt",
        max_length=128,
        padding=True,
        truncation=True
    )
    with torch.no_grad():
        outputs = xlmr_model(**inputs)

    # 使用 [CLS] token 的嵌入作为句子表示
    sentence_embedding = outputs.last_hidden_state[:, 0, :]
    print(f"{lang}: embedding shape = {sentence_embedding.shape}")

8.2 CJK(中日韩)分词的特殊性

CJK(Chinese、Japanese、Korean)语言的空格分词效果不佳,需要特殊处理。

from transformers import BertTokenizer

# BERT 默认分词器对 CJK 的处理
tokenizer = BertTokenizer.from_pretrained("bert-base-multilingual-cased")

texts = {
    "韩语": "자연어처리",        # 黏着语: 助词/词尾附着在词干上
    "中文": "自然语言处理",        # 孤立语: 每个字都是独立的意义单位
    "日语": "自然言語処理",        # 黏着语 + 汉字/平假名/片假名混用
    "英语": "NaturalLanguageProcessing",
}

for lang, text in texts.items():
    tokens = tokenizer.tokenize(text)
    print(f"{lang}: {tokens}")

8.3 韩语专用预处理

# 考虑韩语黏着语特性的预处理
# 词节 = 词干 + 词缀(可有无穷变化)
# 例: '먹다'、'먹어'、'먹었다'、'먹을'、'먹히다'、'먹이다'

from konlpy.tag import Mecab

def extract_korean_features(text: str) -> dict:
    """从韩语文本中提取语言学特征"""
    mecab = Mecab()
    pos_tags = mecab.pos(text)

    features = {
        "nouns": [],      # 名词(主要信息单元)
        "verbs": [],      # 动词原形
        "adjectives": [], # 形容词原形
        "entities": []    # 专有名词
    }

    for word, tag in pos_tags:
        if tag.startswith('NN'):      # 名词类
            features["nouns"].append(word)
        elif tag.startswith('VV'):    # 动词
            features["verbs"].append(word)
        elif tag.startswith('VA'):    # 形容词
            features["adjectives"].append(word)
        elif tag == 'NNP':            # 专有名词
            features["entities"].append(word)

    return features

text = "삼성전자가 새로운 인공지능 반도체를 개발했다고 발표했습니다."
features = extract_korean_features(text)
print(features)

小测验

Q1. BPE 分词是如何解决 OOV 问题的?

答案: BPE 通过反复合并频繁出现的字符对来构建子词词表。即便是未见过的词,也能被分解为已学到的子词单元,从而将 OOV 降到最低。

解析: 传统的基于词的词表,对训练时未出现过的词会处理为 [UNK],这就是 OOV 问题。BPE 从字符层级出发,合并频繁出现的字符对,因此新造词或复合词也都能用已有子词的组合来表示。例如即使训练词表中没有"한국어NLP",也能被分解为"한국어"、"NLP"或更小的单位。

Q2. BERT 的 MLM 和 NSP 预训练任务分别是什么?

答案: MLM 是将输入 token 中的 15% 随机掩码,并预测原始 token 的任务;NSP 是对两个句子是否实际连续进行二分类的任务。

解析: MLM 让模型学习双向上下文,使 BERT 能够深入理解语境。被掩码的 15% 中,80% 替换为 [MASK],10% 替换为随机 token,10% 保持原始 token 不变。NSP 让模型学习句子间的关系,但后来 RoBERTa 等模型发现 NSP 对性能提升没有贡献,因而将其移除。

Q3. 在 RAG 中同时使用 bi-encoder 和 cross-encoder 的两阶段检索有什么好处?

答案: Bi-encoder 独立地对查询和文档进行编码,可以实现快速的 ANN 检索;Cross-encoder 则将查询-文档对一起处理,计算出精确的相关性得分。将两个阶段结合,就能同时兼顾速度和准确度。

解析: 只使用 Bi-encoder 虽然快,但无法捕捉查询与文档之间的交互,准确度较低。Cross-encoder 虽然准确,但需要对每一对文档都进行计算,因而速度较慢。Two-stage 方式先用 bi-encoder 筛选出候选集(20-100 个),再用 cross-encoder 进行重排序,在准确度和效率之间取得平衡。

Q4. 为什么 SBERT 在计算句子相似度时比 BERT 更高效?

答案: SBERT 用 Siamese/Triplet 网络把句子编码为固定大小的向量,因此计算相似度时只需对预先算好的向量做余弦相似度计算即可。而 BERT 需要把两个句子拼接起来,每次都要重新做一次前向传播。

解析: 要计算 n 个句子两两之间的相似度,BERT 需要 n(n-1)/2 次前向传播(约需 65 小时),而 SBERT 只需 n 次编码,之后只做向量运算即可(约需 5 秒),快了数千倍。这在语义搜索、聚类等需要大规模两两比较的任务中尤其实用。

Q5. 为什么韩语 NLP 中的黏着语特性要求与英语不同的处理方式?

答案: 韩语是词干与助词、词尾、词缀结合而成的黏着语,词形变化非常丰富。'먹다'、'먹어요'、'먹었습니다' 都派生自同一个词干,因此必须通过形态素分析提取出词干,才能将它们识别为同一个概念。

解析: 英语的词形变化相对简单,如 'eat'、'eats'、'eating',用简单的词干提取即可处理。而韩语的一个词干可以与数百种词缀组合,因此形态素分析器(MeCab、Okt 等)必不可少。此外,按空格切分时助词会附着在词干上,导致 '인공지능의'、'인공지능이'、'인공지능을' 被当作三个不同的词处理,这也是一个问题。


结语

本指南结合实战代码,覆盖了从文本预处理、BPE 分词、Word2Vec/FastText 词嵌入、BERT 微调、RAG 流水线到多语言处理等 NLP 的核心主题。韩语 NLP 的关键在于结合黏着语特性做形态素分析;而在 RAG 系统中,采用两阶段检索(bi-encoder + cross-encoder)可以同时兼顾速度和准确度。

현재 단락 (1/473)

自然语言处理(NLP)是让计算机理解和生成人类语言的核心 AI 技术。本指南从文本预处理的基础出发,系统覆盖 BERT 微调、RAG 流水线构建、多语言处理等内容,并配有实战代码。

작성 글자: 0원문 글자: 17,172작성 단락: 0/473