در نگاه اول، با ظهور مدلهای زبانی بزرگ شاید تحلیلهای گرامری کلاسیک قدیمی به نظر برسند؛ اما در عمل پایپلاینهای مدرن استخراج داده، اعتبارسنجی خروجیهای ساختاریافتهی مدلها و سیستمهای پرسشوپاسخ متنی همچنان به درک دقیق نقشهای دستوری و پیوندهای کلمات وابستهاند. بدون برچسبگذاری نقش دستوری (POS Tagging)، حتی الگوریتمهای ریشهیابی کلمات (Lemmatization) نیز نمیتوانند تفاوت میان اسم و فعل را تشخیص دهند.
در این درس ساختار گرامری متن را از دو زاویه بررسی میکنیم: برچسبگذاری نقش دستوری و تجزیهی نحوی (Syntactic Parsing). کار را با پیادهسازی خطوط مبنای آماری مثل مدل مارکوف پنهان (HMM) و الگوریتم ویتربی شروع میکنیم و در نهایت به ابزارهای استاندارد صنعتی مانند spaCy میرسیم تا بتوانید روابط کلمات را بهصورت ساختار درختی در پروژههای واقعی تحلیل کنید.
دستور زبان مدتی از مد افتاده بود. تا اینکه همهی پایپلاینهای LLM برای اعتبارسنجی استخراج ساختاریافته به آن نیاز پیدا کردند و دوباره به صحنه برگشت.
صورت مسئله
در درس 01 قول دادیم که ریشهیابی به روش لماتیزاسیون (lemmatization) به برچسب نقش دستوری کلمات نیاز دارد. ریشهیاب بدون دانستن اینکه running فعل است، نمیتواند آن را به run تقلیل دهد. همچنین اگر نداند better صفت است، نمیتواند آن را به good برساند.
پشت آن وعده، یک حوزهی علمی کامل پنهان شده بود. برچسبگذاری نقش دستوری (part-of-speech tagging یا POS tagging) دستهبندیهای گرامری را به کلمات نسبت میدهد. تجزیهی نحوی (syntactic parsing) ساختار درختی جمله را بازسازی میکند: اینکه کدام کلمه توصیفکنندهی کدام است، و کدام فعل چه متممها و شناسههایی را هدایت میکند. پردازش زبان طبیعی کلاسیک بیست سال را صرف صیقل دادن این دو مفهوم کرد. سپس یادگیری عمیق (deep learning) هر دوی آنها را به یک مسئلهی دستهبندی توکن (token) روی یک ترنسفورمر (transformer) پیشآموزشدیده تبدیل کرد و جامعهی پژوهشی سراغ موضوعات بعدی رفت.
اما جامعهی مهندسی کاربردی کنار نکشید. زیر کاپوتِ تکتک پایپلاینهای استخراج ساختاریافته، هنوز از برچسبهای دستوری و درختهای وابستگی استفاده میشود. خروجیهای JSON تولیدشده توسط LLM در برابر قیود گرامری اعتبارسنجی میشوند. سیستمهای پرسش و پاسخ، پرسمانها را با تجزیهی وابستگی تفکیک میکنند. ابزارهای ارزیابی کیفیت ترجمهی ماشینی نیز همترازی درختهای تجزیه را میسنجند.
دانستن این مفاهیم ارزشمند است. در این درس با مجموعهبرچسبها، خطوط مبنا و نقطهای که دیگر نباید از صفر کد بزنید و باید سراغ spaCy بروید، آشنا میشوید.
مفهوم
برچسبگذاری نقش دستوری (POS tagging) به هر توکن یک دستهبندی گرامری اختصاص میدهد. مجموعهبرچسب Penn Treebank (PTB) گزینهی پیشفرض در زبان انگلیسی است؛ شامل 36 برچسب با تمایزهایی که شاید برای خوانندهی عادی بیشازحد جزئی به نظر برسد: NN برای اسم مفرد، NNS برای اسم جمع، NNP برای اسم خاص مفرد، VBD برای فعل زمان گذشته، VBZ برای فعل سومشخص مفرد حال، و مواردی از این دست. در مقابل، مجموعهبرچسب Universal Dependencies (UD) کلیتر است (17 برچسب) و به زبان خاصی وابسته نیست؛ به همین دلیل به گزینهی پیشفرض در کارهای چندزبانه تبدیل شده است.
The/DET cats/NOUN were/AUX running/VERB at/ADP 3pm/NOUN ./PUNCT
تجزیهی نحوی یک ساختار درختی تولید میکند. برای این کار دو شیوهی اصلی وجود دارد:
- تجزیهی سازهای (Constituency parsing). عبارتهای اسمی، فعلی و حرفاضافهای درون یکدیگر لانه میگزینند. خروجی این روش، درختی از دستهبندیهای غیرپایانهای (NP، VP، PP) است که کلمات، برگهای آن هستند.
- تجزیهی وابستگی (Dependency parsing). هر کلمه دقیقاً یک کلمهی هسته (head) دارد که به آن وابسته است و نوع این رابطه با یک برچسب گرامری مشخص میشود. خروجی این روش، درختی است که هر یال آن یک سهتایی بهصورت (هسته، وابسته، رابطه) است.
در دههی 2010 تجزیهی وابستگی پیروز میدان شد، چرا که بهخوبی روی زبانهای گوناگون—بهویژه زبانهایی با ترتیب آزاد کلمات—تعمیم مییابد.
running is ROOT
cats is nsubj of running
were is aux of running
at is prep of running
3pm is pobj of at
پیادهسازی از صفر
گام 1: خط مبنا بر اساس پرتکرارترین برچسب
سادهترین برچسبزن کارآمد: برای هر کلمه، همان برچسبی را پیشبینی کنید که در دادههای آموزشی بیشترین تکرار را داشته است.
from collections import Counter, defaultdict
def train_mft(train_examples):
word_tag_counts = defaultdict(Counter)
all_tags = Counter()
for tokens, tags in train_examples:
for token, tag in zip(tokens, tags):
word_tag_counts[token.lower()][tag] += 1
all_tags[tag] += 1
word_best = {w: c.most_common(1)[0][0] for w, c in word_tag_counts.items()}
default_tag = all_tags.most_common(1)[0][0]
return word_best, default_tag
def predict_mft(tokens, word_best, default_tag):
return [word_best.get(t.lower(), default_tag) for t in tokens]
این خط مبنا روی پیکرهی Brown به دقت حدود 85% میرسد. عالی نیست، اما کف دقتی است که هیچ مدل جدیای نباید پایینتر از آن بیاید.
گام 2: برچسبزن HMM دوتایی (bigram)
احتمال توأم توالی را مدلسازی کنید:
دو جدول داریم: احتمالهای انتقال (احتمال یک برچسب به شرط برچسب قبلی) و احتمالهای انتشار (احتمال یک کلمه به شرط برچسب آن). هر دو جدول را از روی شمارشها و با هموارسازی لاپلاس (Laplace smoothing) تخمین میزنیم. سپس رمزگشایی را با الگوریتم ویتربی (Viterbi؛ برنامهنویسی پویا روی شبکهی حالتهای برچسب) انجام میدهیم.
import math
def train_hmm(train_examples, alpha=0.01):
transitions = defaultdict(Counter)
emissions = defaultdict(Counter)
tags = set()
vocab = set()
for tokens, ts in train_examples:
prev = "<BOS>"
for token, tag in zip(tokens, ts):
transitions[prev][tag] += 1
emissions[tag][token.lower()] += 1
tags.add(tag)
vocab.add(token.lower())
prev = tag
transitions[prev]["<EOS>"] += 1
return transitions, emissions, tags, vocab
def log_prob(table, given, key, smooth_denom, alpha):
return math.log((table[given].get(key, 0) + alpha) / smooth_denom)
def viterbi(tokens, transitions, emissions, tags, vocab, alpha=0.01):
tags_list = list(tags)
n = len(tokens)
V = [[0.0] * len(tags_list) for _ in range(n)]
back = [[0] * len(tags_list) for _ in range(n)]
for j, tag in enumerate(tags_list):
em_denom = sum(emissions[tag].values()) + alpha * (len(vocab) + 1)
tr_denom = sum(transitions["<BOS>"].values()) + alpha * (len(tags_list) + 1)
tr = log_prob(transitions, "<BOS>", tag, tr_denom, alpha)
em = log_prob(emissions, tag, tokens[0].lower(), em_denom, alpha)
V[0][j] = tr + em
back[0][j] = 0
for i in range(1, n):
for j, tag in enumerate(tags_list):
em_denom = sum(emissions[tag].values()) + alpha * (len(vocab) + 1)
em = log_prob(emissions, tag, tokens[i].lower(), em_denom, alpha)
best_prev = 0
best_score = -1e30
for k, prev_tag in enumerate(tags_list):
tr_denom = sum(transitions[prev_tag].values()) + alpha * (len(tags_list) + 1)
tr = log_prob(transitions, prev_tag, tag, tr_denom, alpha)
score = V[i - 1][k] + tr + em
if score > best_score:
best_score = score
best_prev = k
V[i][j] = best_score
back[i][j] = best_prev
last_best = max(range(len(tags_list)), key=lambda j: V[n - 1][j])
path = [last_best]
for i in range(n - 1, 0, -1):
path.append(back[i][path[-1]])
return [tags_list[j] for j in reversed(path)]
مدل HMM دوتایی روی پیکرهی Brown به دقت حدود 93% میرسد. جهش از 85% به 93% عمدتاً ناشی از احتمالهای انتقال است؛ مدل یاد میگیرد که توالی DET NOUN بسیار رایج و NOUN DET نادر است.
گام 3: چرا برچسبزنهای امروزی از این مدلها بهترند؟
احتمالهای انتقال و انتشار ماهیتی محلی دارند. آنها نمیتوانند درک کنند که کلمهی saw در جملهی «I bought a saw» اسم است، اما در «I saw the movie» نقش فعل دارد. مدل میدان تصادفی شرطی (CRF) با ویژگیهای دلخواه (پسوند، شکل ظاهری کلمه، کلمات قبل و بعد، و خود کلمه) به دقت حدود 97% میرسد. یک مدل BiLSTM-CRF یا ترنسفورمر هم دقت را به بالای 98% میرساند.
سقف دقت در این وظیفه را میزان عدمتوافق میان ارزیابهای انسانی تعیین میکند. تحلیلگران انسانی روی مجموعهدادهی Penn Treebank در حدود 97% مواقع با یکدیگر توافق دارند. مدلهایی که از 98% عبور میکنند، به احتمال زیاد دچار بیشبرازش (overfitting) روی مجموعهی آزمون شدهاند.
گام 4: طرح کلی تجزیهی وابستگی
پیادهسازی کامل تجزیهی وابستگی از صفر خارج از دامنه (scope) این درس است؛ مرجع استاندارد آن در کتاب Jurafsky and Martin آمده است. دو خانوادهی کلاسیک و اصلی که باید بشناسید:
- تجزیهکنندههای مبتنی بر انتقال (Transition-based) (مانند arc-eager و arc-standard) شبیه یک تجزیهکنندهی انتقال-کاهش (shift-reduce) عمل میکنند: توکنها را میخوانند، آنها را داخل یک پشته (stack) میگذارند (عمل shift)، و با اجرای عملیات کاهش (reduce)، یالهای وابستگی را میسازند. رمزگشایی حریصانه در این روش بسیار سریع است. پیادهسازی کلاسیک آن ابزار MaltParser است و نسخهی مدرن مبتنی بر شبکه عصبی آن را چن و منینگ (Chen and Manning) ارائه دادند.
- تجزیهکنندههای مبتنی بر گراف (Graph-based) (مانند الگوریتم آیزنر و روش دوجفتخطی Dozat-Manning) به هر یالِ ممکن میان هسته و وابسته امتیاز میدهند و درخت پوشای بیشینه را انتخاب میکنند. این شیوه کندتر اما دقیقتر است.
در بیشتر کارهای کاربردی، کافی است spaCy را فراخوانی کنید:
import spacy
nlp = spacy.load("en_core_web_sm")
doc = nlp("The cats were running at 3pm.")
for token in doc:
print(f"{token.text:10s} tag={token.tag_:5s} pos={token.pos_:6s} dep={token.dep_:10s} head={token.head.text}")
The tag=DT pos=DET dep=det head=cats
cats tag=NNS pos=NOUN dep=nsubj head=running
were tag=VBD pos=AUX dep=aux head=running
running tag=VBG pos=VERB dep=ROOT head=running
at tag=IN pos=ADP dep=prep head=running
3pm tag=NN pos=NOUN dep=pobj head=at
. tag=. pos=PUNCT dep=punct head=running
اگر ستون dep را از پایین به بالا بخوانید، ساختار دستوری جمله بهروشنی نمایان میشود.
استفاده از ابزارهای واقعی
همهی کتابخانههای تجاری NLP، ابزارهای برچسبگذاری POS و تجزیهی وابستگی را بهصورت بخشی از پایپلاین استاندارد خود ارائه میدهند.
- spaCy (مدلهای
en_core_web_sm/md/lg/trf). سریع، دقیق و یکپارچهشده با توکنسازی، بازشناسی هویتهای نامدار (NER) و ریشهیابی. ویژگیها:token.tag_(برچسبهای Penn)،token.pos_(برچسبهای UD) وtoken.dep_(رابطهی وابستگی). - Stanford NLP (stanza). جانشین استنفورد برای CoreNLP؛ پیشرو در ارائهی بهترین نتایج روی بیش از 60 زبان.
- trankit. مبتنی بر ترنسفورمر، با دقت بسیار خوب در برچسبهای UD.
- NLTK. تابع
pos_tag. کار را راه میاندازد، اما کندتر و قدیمیتر است. برای مقاصد آموزشی گزینهی خوبی به شمار میرود.
چرا این مفاهیم در سال 2026 هنوز مهم هستند؟
- ریشهیابی (Lemmatization). طبق درس 01، برای لماتیزاسیون درست همیشه به برچسب POS نیاز دارید؛ بدون استثنا.
- استخراج ساختاریافته از خروجیهای LLM. برای بررسی اینکه آیا جملهی تولیدشده قیود گرامری را رعایت کرده است یا نه (مثل مطابقت فاعل و فعل، یا وجود وابستههای ضروری).
- تحلیل احساسات مبتنی بر جنبه (Aspect-based sentiment). درختهای وابستگی مشخص میکنند کدام صفت در حال توصیف کدام اسم است.
- درک پرسمان (Query understanding). عبارتی مثل «movies directed by Wes Anderson starring Bill Murray» از طریق تجزیهی نحوی به قیود ساختاریافته تجزیه میشود.
- انتقال بینزبانی (Cross-lingual transfer). برچسبها و روابط وابستگی UD مستقل از زبان هستند و امکان تحلیل ساختاریافتهی صفر-داده (zero-shot) را روی زبانهای جدید فراهم میکنند.
- پایپلاینهای کممصرف و سبک. اگر نمیتوانید یک مدل ترنسفورمر را مستقر کنید، ترکیب برچسبهای POS، تجزیهی وابستگی و فهرست واژگان (gazetteer) کار را بهطرز چشمگیری پیش میبرد.
خروجی این درس
محتوای زیر را در فایل outputs/skill-grammar-pipeline.md ذخیره کنید:
---
name: grammar-pipeline
description: Design a classical POS + dependency pipeline for a downstream NLP task.
version: 1.0.0
phase: 5
lesson: 07
tags: [nlp, pos, parsing]
---
Given a downstream task (information extraction, rewrite validation, query decomposition, lemmatization), you output:
1. Tagset to use. Penn Treebank for English-only legacy pipelines, Universal Dependencies for multilingual or cross-lingual.
2. Library. spaCy for most production, stanza for academic-grade multilingual, trankit for highest UD accuracy. Name the specific model ID.
3. Integration pattern. Show the 3-5 lines that call the library and consume the needed attributes (`.pos_`, `.dep_`, `.head`).
4. Failure mode to test. Noun-verb ambiguity (`saw`, `book`, `can`) and PP-attachment ambiguity are the classical traps. Sample 20 outputs and eyeball.
Refuse to recommend rolling your own parser. Building parsers from scratch is a research project, not an application task. Flag any pipeline that consumes POS tags without handling lowercase/uppercase variants as fragile.
تمرینها
- ساده. با استفاده از خط مبنای پرتکرارترین برچسب روی یک پیکرهی برچسبخوردهی کوچک (مثل زیرمجموعهی Brown در NLTK)، دقت مدل را روی جملات ارزیابی (held-out) بسنجید. بررسی کنید که آیا به نتیجهی حدود 85% میرسید یا خیر.
- متوسط. مدل HMM دوتایی بالا را آموزش دهید و دقت (Precision) و بازخوانی (Recall) را به تفکیک برچسبها گزارش کنید. مدل HMM بیشترین خطا را روی کدام برچسبها دارد؟
- سخت. با استفاده از تجزیهی وابستگی spaCy، سهگانههای فاعل-فعل-مفعول (SVO) را از یک نمونهی 1000 جملهای استخراج کنید. صحت خروجی را روی 50 سهگانهی برچسبخوردهی دستی ارزیابی کنید. مواردی را که استخراج در آنها شکست میخورد ثبت کنید (معمولاً جملات مجهول، عبارات عطفی و فاعلهای محذوف).
اصطلاحات کلیدی
| اصطلاح | در گفتوگوهای عامیانه | معنای دقیق فنی |
|---|---|---|
| POS tag | نوع کلمه | دستهبندی گرامری کلمه. مجموعهبرچسب PTB دارای 36 برچسب و UD دارای 17 برچسب است. |
| Penn Treebank | مجموعهبرچسب استاندارد | ویژهی زبان انگلیسی. تفکیک بسیار دقیق زمانهای افعال و مفرد یا جمع بودن اسامی. |
| Universal Dependencies | مجموعهبرچسب چندزبانه | کلیتر از PTB؛ مستقل از زبان و گزینهی پیشفرض برای کارهای چندزبانه. |
| Dependency parse | درخت جمله | هر کلمه یک هسته دارد و هر یال نشاندهندهی یک رابطهی گرامری است. |
| Viterbi | برنامهنویسی پویا | یافتن محتملترین توالی برچسبها بر اساس مقادیر انتشار و انتقال. |
برای مطالعهی بیشتر
- Jurafsky and Martin — Speech and Language Processing، فصلهای 8 و 18 — مرجع استاندارد برچسبگذاری POS و تجزیهی نحوی.
- پروژهی Universal Dependencies — مجموعهبرچسب و پیکرههای درختی چندزبانه که تمام تجزیهکنندههای چندزبانه بر پایهی آن ساخته شدهاند.
- راهنمای ویژگیهای زبانی spaCy — مستندات کاربردی برای تمامی مشخصههای موجود روی شیء
Token. - چن و منینگ (2014). یک تجزیهکنندهی وابستگی سریع و دقیق با شبکههای عصبی — مقالهای که تجزیهکنندههای مبتنی بر شبکه عصبی را فراگیر کرد.
