فهرست مطالب
در مقاله قبل با ایست واژه آشنا شدیم و فهمیدیم این کلمات اطلاعات خاصی در اختیار ما نمیگذارند، پس بهتر است آنها را در هنگام پیشپردازش متون حذف کنیم. در ادامه آموزش مراحل پیشپردازش و آمادهسازی داده، در این قسمت درباره توکنسازی و کتابخانه NLTK صحبت میکنیم. کتابخانه NLTK از مهمترین کتابخانههای پایتون برای متنکاوی است.
توکن سازی (Tokenization) چیست؟
به فرایند تبدیل یک متن دلخواه به لیستی از کلمات، توکنسازی (Tokenization) و به هر کلمه یک توکن (Token) گفته میشود یا به زبان سادهتر، توکنسازی یک متن را به کلمات تشکیلدهنده آن تقسیم میکند.
در مقاله قبلی نیز بررسی کردیم که چگونه یک متن را بر اساس کاراکتر space به کمک دستور
text.split(' ')
به تعدادی کلمه تقسیم کنیم تا فراوانی هر کلمه را به دست آوریم؛ این عمل همان توکنسازی است و هر کلمهای که فراوانی آن را محاسبه کردیم، یک توکن نامیده میشود. تا این مرحله همه چیز ساده به نظر میرسد، اما هنوز برای قضاوت کردن زود است و باید جزئیات بیشتری را بررسی کنیم.
اگر اول راه پردازش زبان طبیعی هستید و میخواهید کار بر روی پروژههای واقعی NLP را یاد بگیرید، دوره آموزش پردازش زبان طبیعی مقدماتی را از دست ندهید.
اهمیت علائم نگارشی (Punctuation)
فرض کنید که میخواهیم دو جمله “Let’s eat, Grandpha.” و “Let’s eat.” را بر اساس کاراکتر space جدا کنیم و توکنهای تشکیلدهنده این دو جمله را به دست آوریم. به جدول زیر دقت کنید:
تعداد توکن | توکنسازی بر اساس کاراکتر space | جمله |
---|---|---|
3 | Let's - eat, - Grandpa. | Let's eat, Grandpa. |
2 | Let's - eat. | Let's eat. |
همانطور که در جدول میبینیم، "eat," و "eat." بهعنوان دو توکن متفاوت در نظر گرفته شده است که البته این کار به دو دلیل درست نیست:
- هیچ دلیلی برای تمایز قائل شدن بین دو توکن "eat," و "eat." وجود ندارد. هر دو مربوط به فعل "eat" بوده و معنی یکسان دارند.
- اگر به این مسئله کلیتر نگاه کنیم، متوجه میشویم که یک مشکل دیگر داریم. آن مشکل، اندازه تعداد واژگان یا تعداد توکنهای منحصربهفرد (Unique Tokens) است که یکی از پارامترهای مهم در هر پروژه NLP است؛ زیرا با افزایش تعداد واژگان، به قدرت محاسباتی بیشتری اعم از حافظه (Memory)، CPU و... نیاز داریم؛ بنابراین همیشه بهترین کار این است که تعداد واژگان یا توکنهای منحصربهفرد یک متن را تا جایی که ممکن است به حداقل برسانیم.
برای حل این مشکل میتوانیم از یک روش جایگزین و بهتر برای مدیریت علائم نگارشی (Punctuation) استفاده کنیم. در این روش، هنگام توکنسازی، علائم نگارشی را از کلمات جدا کرده و هر یک از علامتهای نگارشی را بهعنوان یک توکن مجزا در نظر میگیریم؛ بنابراین، جدول قبلی به این شکل تغییر پیدا میکند:
تعداد توکن | توکنسازی بر اساس کاراکتر space | جمله |
---|---|---|
6 | Let - 's - eat - , - Grandpa - . | Let's eat, Grandpa. |
4 | Let - 's - eat - . | Let's eat. |
بهاینترتیب، برای هر دو جمله، برای “eat” یک توکن منحصربهفرد به دست میآید.
یک نکته دیگر در خصوص علائم نگارشی قابلذکر است و آن هم اهمیت استفاده درست از علائم نگارشی مانند کاما “,” است. جمله
"Let’s eat, Grandpa."
به معنای “پدربزرگ، بیا (غذا) بخوریم” است اما جمله
"Let’s eat Grandpa."
معنای کاملاً متفاوت و البته ترسناکی دارد؛ زیرا پیشنهاد میدهد که “بیا پدربزرگ را بخوریم.”!!!
با معادل معروف فارسی آن نیز حتماً آشنا هستیم. "بخشش لازم نیست، اعدامش کنید." و "بخشش، لازم نیست اعدامش کنید." معنای جمله با جابهجایی تنها یک ویرگول دچار تغییر شد.
بهطورکلی، برای یک توکنسازی مناسب باید دو نکته را رعایت کنیم:
- علائم نگارشی:
eat, => ["eat", ","]
- کلمات مخفف (Contractions):
can't => ["can", "'t"] or ["can", "'", "t"] ; doesn't => ["doesn", "'t"] or ["doesn", "'", "t"]
در زبان انگلیسی بیش از 100 کلمه مخفف مانند it’s، she’d و must’ve داریم که برای شناسایی آنها میتوانیم از کتابخانه پایتون (Contractions Python Library) استفاده کرده و کلمات اصلی پیش از مخفف کردن را به دست آوریم. این کار باعث میشود که دیگر توکنهایی مانند “s” و “nt” را حذف کرده و معادل آنها یعنی “is/has” و “not” را داشته باشیم.
باتوجهبه این چالشها، نتیجه میگیریم که توکنسازی کار سادهای نیست و باید رویکردی هوشمندانهتر در مقایسه با تقسیم متن بر اساس کاراکتر space داشته باشیم. البته که همه کتابخانههای مهم در حوزه پردازش زبان طبیعی، Tokenizers مناسبی را ارائه میدهند! بهعنوانمثال، میتوانیم کتابخانه NLTK را بهعنوان یکی از آنها بررسی کنیم.
در صورتی که با NLP آشنایی کافی دارید، دوره آموزش متن کاوی فارسی با شبکههای عصبی را تهیه کنید. در این دوره، با تمرکز روی زبان فارسی، جدیدترین تکنیکهای پردازش زبان طبیعی با کدنویسی فراوان آموزش داده میشود.
معرفی کتابخانه NLTK
NLTK (Natural Language Toolkit)، یک کتابخانه به زبان پایتون است که اولینبار در سال 2001 منتشر شد و تسکهای زیادی مانند دستهبندی متن (Text Classification)، توکنسازی، ریشهیابی (Stemming)، برچسبگذاری (Tagging)، تجزیه نحوی (Parsing) و سایر تسکهای مربوط به تحلیل معنایی (Semantic Analysis) را پوشش میدهد. برای مشاهده وبسایت اصلی کتابخانه NLTK، به این آدرس http://www.nltk.org مراجعه کنید.
در این مجموعه مقاله آموزشی، یاد میگیریم NLTK چیست و از آن برای سه تسک زیر استفاده میکنیم:
- پیداکردن Tokenizer مناسب
- مدیریت N-gramsها
- تکمیل لیست ایستواژهها (Stop Words)
نحوه نصب کتابخانه NLTK را میتوانید در آدرس https://www.nltk.org/install.html دنبال کنید. کتابخانه NLTK به هر دو روش conda و pip قابل نصب است. پس از نصب کتابخانه، برای استفاده از ابزارها و توابع خاص آن باید مجموعهدادهها و مدلهای لازم را دانلود کنیم.
برای این کار میتوانیم این دو خط کد را در نوتبوک پایتون یا terminal خود اجرا کنیم:
import nltk
nltk.download('popular')
پس از اجرای این دو خط کد، برخی از متداولترین و پرکاربردترین مجموعهدادهها و مدلها بر روی سیستم ما دانلود میشود.
کتابخانه NLTK پکیجی به نام Tokenizer دارد که به ما امکان استفاده از چندین Tokenizers مختلف را ارائه میدهد. میتوانیم برای اطلاعات تکمیلی بیشتر به مستندات NLTK مراجعه کنیم. برخی از این توکنسازها برای متنهای خاصی کاربرد دارند. بهعنوانمثال، TweetTokenizer برای مدیریت توییتها و WordPunctTokenizer برای مدیریت Punctuation به کار میروند. قرار است که در ادامه مقاله از WordPunctTokenizer کتابخانه NLTK استفاده کنیم. اگر با یک متن ساده شروع کنیم، این دو خط کد را اجرا میکنیم:
from nltk.tokenize import WordPunctTokenizer
tokens = WordPunctTokenizer().tokenize("Let's eat your soup, Grandpa.")
متغیر tokens شامل لیستی از تمامی Punctuation و Contractions است که در جمله
“Let’s eat your soup, Grandpa” به کار رفتهاند:
["Let", "'", "s", "eat", "your", "soup", ",", "Grandpa", "."]
دقت کنید که علامت apostrophe به شکل " ‘ " در "Let’s"، بهعنوان یک توکن مجزا در نظر گرفته شده است.
اگر از WordPunctTokenizer کتابخانه NLTK برای متن مقاله Earth ویکیپدیا استفاده کنیم:
from nltk.tokenize import WordPunctTokenizer
text = wikipedia_page('Earth')
tokens = WordPunctTokenizer().tokenize(text)
print(Counter(tokens).most_common(20))
نتایج زیر را بهعنوان پرکاربردترین توکنها خواهیم داشت. دقت شود که این نتایج بر اساس متن اصلی و بدون حذف کردن ایستواژهها به دست آمدهاند:
print(Counter(tokens).most_common(20))
> [('the', 597), (',', 565), ('.', 473), ('of', 339), ('and', 253), ('Earth', 211), ('is', 177), ('to', 165), ('in', 135), ('s', 131), ("'", 130), ('a', 122), ('(', 110), ('The', 98), ('-', 82), ('from', 68), ('that', 66), ('by', 65), ('as', 61), ('with', 55)]
بهاینترتیب، علائمی مانند نقطه "."، کاما "," و خط تیره "-" بهصورت توکنهای مجزا، مشخص شدهاند.
توکنسازی بر اساس کاراکترها و هجاها (Syllables)
برای توکنسازی در کتابخانه NLTK هیچ محدودیتی نداریم. یعنی لازم نیست که همیشه بر اساس کاراکتر space و سایر علائم نگارشی توکنسازی کنیم. به طور مثال، گاهی اوقات میتوانیم یک متن را بهصورت لیستی از هجاهای آن تقسیم کنیم.
جمله
"Earth is the third planet from the sun."
را میتوانیم به روشهای مختلفی توکنسازی کنیم. سه روش از این چند روش، اینجا ذکر شده است:
- توکنسازی بر اساس کلمات (Words):
Earth; is; the; third; planet; from; the; Sun; .;
(دقت شود که علامت نقطه “.” ، بهعنوان یک توکن مجزا لحاظ شده است.)
- توکنسازی بر اساس زیر کلمات (Subwords):
Ear; th; is; the; thi; rd; pla; net; from; the; Sun; .;
- توکنسازی بر اساس کاراکترها (Characters):
E;a;r;t;h; ;i;s; ;t;h;e; ;t;h;i;r;d; .....
(دقت شود که کاراکتر space، بهعنوان یک توکن مجزا لحاظ شده است.)
اگر متن مقاله Earth را بر اساس کاراکترها توکنسازی کنیم، کد و خروجی 10 کاراکتر پرکاربرد آن، به این صورت است:
# example of character tokenization
char_tokens = [ c for c in text ]
print(Counter(char_tokens).most_common(10))
> [(' ', 8410), ('e', 4979), ('t', 3877), ('a', 3762), ('i', 3040), ('o', 3024), ('r', 2793), ('s', 2777), ('n', 2756), ('h', 2007)
اینکه از چه روشی برای توکنسازی استفاده کنیم، کاملاً به نوع تسک و هدفی که داریم بستگی دارد. بهطورکلی، توکنسازی بر اساس کلمات متداولترین روش است، اما برای بررسی املای متون (Spell Checking)، توکنسازی بر اساس کاراکتر بهترین انتخاب است. توکنسازی بر اساس زیر کلمه نیز در مدلهای جدید NLP مانند BERT استفاده میشود.
البته برای زبانهای خاصی مانند چینی (Chinese) و ژاپنی (Japanese) از روشهای جایگزین برای توکنسازی استفاده میشود. در این روشها برای مشخصکردن مرز بین کلمات، از کاراکتر space استفاده نمیشود. همچنین، زبانی مانند زبان عربی (Arabic) نیز چالشهای خاص خود را دارد که در مقاله Arabic Tokenization Systems توسط Mohammed Attia توضیح داده شده است.
توکنسازی بر اساس N-Grams در کتابخانه NLTK
برخی از کلمات با هم و به طور گروهی بهتر درک میشوند. یعنی بهجای آنکه یک کلمه را بهتنهایی در نظر بگیریم، باید دنباله یا گروهی از کلمات را با هم لحاظ کنیم تا معنا و مفهوم یک عبارت را کاملتر داشته باشیم. مانند: deep learning، New York، love at first sight و The New England Journal of Medicine.
بنابراین، هنگام توکنسازی یک متن در کتابخانه NLTK، بهتر است که دنباله یا گروهی از کلمات مانند گروههای دو کلمهای (bigrams)، گروههای سه کلمهای (trigrams) و … را شناسایی کنیم. بهطورکلی به دنباله یا گروهی از کلمات که بهعنوان یک توکن در نظر گرفته میشوند، n-gram میگویند که عدد n نمایانگر تعداد کلمات موجود در آن گروه است.
میتوانیم n-gramهای یک متن را به کمک NLTK ngrams به دست آوریم. در کد زیر، bigrams و trigrams یک جمله ساده را مشخص کردهایم:
from nltk import ngrams
from nltk.tokenize import WordPunctTokenizer
text = "How much wood would a woodchuck chuck if a woodchuck could chuck wood?"
# Tokenize
tokens = WordPunctTokenizer().tokenize(text)
# bigrams
bigrams = [w for w in ngrams(tokens,n=2)]
print(bigrams)
[('How', 'much'), ('much', 'wood'), ('wood', 'would'), ('would', 'a'), ('a', 'woodchuck'), ('woodchuck', 'chuck'), ('chuck', 'if'), ('if', 'a'), ('a', 'woodchuck'), ('woodchuck', 'could'), ('could', 'chuck'), ('chuck', 'wood'), ('wood', '?')]
# trigrams
trigrams = ['_'.join(w) for w in ngrams(tokens,n=3)]
print(trigrams)
['How_much_wood', 'much_wood_would', 'wood_would_a', 'would_a_woodchuck', 'a_woodchuck_chuck', 'woodchuck_chuck_if', 'chuck_if_a', 'if_a_woodchuck', 'a_woodchuck_could', 'woodchuck_could_chuck', 'could_chuck_wood', 'chuck_wood_?']
بهراحتی میتوانیم N-Gramها را به کمک “_” به یکدیگر وصل کنیم و توکنهای چند کلمهای بسازیم:
bi_tokens = ['_'.join(w) for w in bigrams]
print(bi_tokens)
['How_much', 'much_wood', 'wood_would', 'would_a', 'a_woodchuck', 'woodchuck_chuck', 'chuck_if', 'if_a', 'a_woodchuck', 'woodchuck_could', 'could_chuck', 'chuck_wood', 'wood_?']
پیشازاین گفته شد که اندازه تعداد واژگان یا تعداد توکنهای منحصربهفرد یک متن، از پارامترهای مهم و حیاتی در هر پروژه NLP است. با درنظرگرفتن bigrams و trigrams و ... اندازه تعداد واژگان بهصورت نمایی رشد میکند که این رشد بر روی نیازمندیهای قدرت پردازش و میزان حافظه، تأثیر زیادی دارد. یکی از راههای کمکردن تعداد واژگان، حذف کلمات بسیار نادر یا بسیار متداول است که البته حد آستانه این پارامترها (نادر یا متداول بودن کلمات)، اغلب در توابع پیشپردازشی کتابخانههای NLP در دسترس است.
تاکنون در مورد ایستواژهها، Tokenizers و ابر کلمات (Word Clouds) صحبت کردیم و نحوه پیادهسازی آنها را دیدیم اما برای فهم عمیقتر و تسلط بیشتر بر جزئیات، لازم است که خودتان نیز این موارد را پیادهسازی کرده و آنها را تمرین کنید.
مراحل تمرین بیشتر، بهصورت گامبهگام، به شرح زیر است:
- یک داده متنی تهیه کنید (این داده میتواند مقاله ویکیپدیا، متنی از پروژه گوتنبرگ یا هر داده متنی دیگری باشد).
- توکنهای متن خود را با استفاده از NLTK WordPunctTokenizer به دست آورید.
- لیست توکنها و فراوانی آنها را بررسی کنید.
- WordCloud را به دو صورت اجرا کرده و نتایج آنها را مقایسه کنید. در حالت اول، آن را با پارامترهای پیشفرض اجرا کنید و در حالت دوم، پارامترهای زیر را تغییر دهید:
- collocations = False
- normalize_plurals = True or False
- include_numbers = True or False
- min_word_length
- stopwords
پس از اجرای هر دو حالت، خروجی آنها را با هم مقایسه کنید.
- ایستواژههای (Stop Words) متن اصلی خود را حذف کنید.
میتوانید پاسخ و پیادهسازی این مراحل را در این Jupyter Notebooks پیدا کنید.
همان طور که مشاهده میکنید، تمامی کدها به زبان برنامهنویسی پایتون هستند. اگر هنوز با این زبان برنامه نویسی آشنا نیستید و به دنبال آموزش پایتون برای علم داده هستید، دوره آموزش پایتون ویژه هوش مصنوعی را در کانال یوتیوب دیتاهاب مشاهده کنید.
- تقسیم یک متن بر اساس کاراکتر space یک راهحل بسیار ابتدایی و ساده است که نکات مربوط به Punctuations و Contractions در آن رعایت نمیشود.
- کتابخانه NLTK پکیجی به نام Tokenizer دارد که به ما امکان استفاده از چندین Tokenizers مختلف را ارائه میدهد. میتوانیم برای اطلاعات تکمیلی بیشتر برای پاسخ به سوال NLTK چیست و چه امکاناتی دارد، به مستندات کتابخانه NLTK مراجعه کنیم. برخی از این توکن سازها برای متنهای خاصی کاربرد دارند؛ بنابراین باید توکنسازی را انتخاب کنیم که برای متن ما مناسب باشد.
- برای توکنسازی هیچ محدودیتی نداریم و ممکن است که با توجه نوع تسک، کاربرد آن و مدلهای جدید NLP مانند BERT، توکنسازی بر اساس کاراکتر یا syllable بهتر و کاربردیتر باشد.
- به دنباله یا گروهی از کلمات که بهعنوان یک توکن در نظر گرفته میشوند، n-gram میگویند که عدد n نمایانگر تعداد کلمات موجود در آن گروه است. میتوانیم n-gramهای یک متن را به کمک ngrams() به دست آوریم.
- همیشه بهخاطر داشته باشد که اندازه تعداد واژگان رابطه مستقیمی باقدرت محاسباتی موردنیاز دارد و بر روی آن اثر میگذارد.
این مقاله هم به پایان رسید و ما دو خبر برای شما داریم. یک خبر بد و یک خبر خوب!
خبر بد این است که با اینکه فهمیدیم کتابخانه NLTK چیست و چه قابلیتهایی دارد، اما کار تمیزکردن داده متنی هنوز به پایان نرسیده، زیرا معمولاً کلمات شکلهای مختلفی دارند. باید به کلمات جمع (plurals)، declensions مانند home و house یا افعالی مانند am، is و are که ریشه همگی آنها to be است، توجه کنیم.
Declension در یک زبان شامل تغییر در اسم، ضمیر، صفت و سایر نقشها برای نشاندادن جنسیت، عدد، یک حالت گرامری یا یک معنای متفاوت است. مانند تغییر ضمایر He و She به his و her.
و اما خبر خوب! در مقاله کتابخانه spacy، دو تکنیک معروف ریشهیابی یعنی Stemming و Lemmatization را برای پیداکردن ریشه کلمات، بررسی میکنیم تا کار تمیزکردن دادههای متنی را ادامه دهیم.