647 lines
31 KiB
Plaintext
647 lines
31 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# وظیفه طبقهبندی متن\n",
|
||
"\n",
|
||
"در این بخش، با یک وظیفه ساده طبقهبندی متن بر اساس مجموعه داده **[AG_NEWS](http://www.di.unipi.it/~gulli/AG_corpus_of_news_articles.html)** شروع میکنیم: تیترهای خبری را به یکی از ۴ دسته زیر طبقهبندی خواهیم کرد: جهان، ورزش، تجارت و علم/فناوری.\n",
|
||
"\n",
|
||
"## مجموعه داده\n",
|
||
"\n",
|
||
"برای بارگذاری مجموعه داده، از API **[TensorFlow Datasets](https://www.tensorflow.org/datasets)** استفاده خواهیم کرد.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 1,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"import tensorflow as tf\n",
|
||
"from tensorflow import keras\n",
|
||
"import tensorflow_datasets as tfds\n",
|
||
"\n",
|
||
"# In this tutorial, we will be training a lot of models. In order to use GPU memory cautiously,\n",
|
||
"# we will set tensorflow option to grow GPU memory allocation when required.\n",
|
||
"physical_devices = tf.config.list_physical_devices('GPU') \n",
|
||
"if len(physical_devices)>0:\n",
|
||
" tf.config.experimental.set_memory_growth(physical_devices[0], True)\n",
|
||
"\n",
|
||
"dataset = tfds.load('ag_news_subset')"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"اکنون میتوانیم با استفاده از `dataset['train']` و `dataset['test']` به بخشهای آموزش و آزمایش مجموعه داده دسترسی پیدا کنیم:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 3,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Length of train dataset = 120000\n",
|
||
"Length of test dataset = 7600\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"ds_train = dataset['train']\n",
|
||
"ds_test = dataset['test']\n",
|
||
"\n",
|
||
"print(f\"Length of train dataset = {len(ds_train)}\")\n",
|
||
"print(f\"Length of test dataset = {len(ds_test)}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"بیایید اولین ۱۰ تیتر جدید را از مجموعه داده خود چاپ کنیم:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 4,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"3 (Sci/Tech) -> b'AMD Debuts Dual-Core Opteron Processor' b'AMD #39;s new dual-core Opteron chip is designed mainly for corporate computing applications, including databases, Web services, and financial transactions.'\n",
|
||
"1 (Sports) -> b\"Wood's Suspension Upheld (Reuters)\" b'Reuters - Major League Baseball\\\\Monday announced a decision on the appeal filed by Chicago Cubs\\\\pitcher Kerry Wood regarding a suspension stemming from an\\\\incident earlier this season.'\n",
|
||
"2 (Business) -> b'Bush reform may have blue states seeing red' b'President Bush #39;s quot;revenue-neutral quot; tax reform needs losers to balance its winners, and people claiming the federal deduction for state and local taxes may be in administration planners #39; sights, news reports say.'\n",
|
||
"3 (Sci/Tech) -> b\"'Halt science decline in schools'\" b'Britain will run out of leading scientists unless science education is improved, says Professor Colin Pillinger.'\n",
|
||
"1 (Sports) -> b'Gerrard leaves practice' b'London, England (Sports Network) - England midfielder Steven Gerrard injured his groin late in Thursday #39;s training session, but is hopeful he will be ready for Saturday #39;s World Cup qualifier against Austria.'\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"classes = ['World', 'Sports', 'Business', 'Sci/Tech']\n",
|
||
"\n",
|
||
"for i,x in zip(range(5),ds_train):\n",
|
||
" print(f\"{x['label']} ({classes[x['label']]}) -> {x['title']} {x['description']}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## بردارسازی متن\n",
|
||
"\n",
|
||
"حالا باید متن را به **اعداد** تبدیل کنیم تا بتوان آن را بهصورت تنسورها نمایش داد. اگر بخواهیم متن را در سطح کلمات نمایش دهیم، باید دو کار انجام دهیم:\n",
|
||
"\n",
|
||
"* استفاده از یک **توکنایزر** برای شکستن متن به **توکنها**.\n",
|
||
"* ساخت یک **واژگان** از این توکنها.\n",
|
||
"\n",
|
||
"### محدود کردن اندازه واژگان\n",
|
||
"\n",
|
||
"در مثال مجموعه داده AG News، اندازه واژگان نسبتاً بزرگ است و بیش از ۱۰۰ هزار کلمه دارد. بهطور کلی، ما به کلماتی که بهندرت در متن ظاهر میشوند نیازی نداریم — فقط چند جمله آنها را خواهند داشت و مدل از آنها چیزی یاد نخواهد گرفت. بنابراین، منطقی است که اندازه واژگان را با ارسال یک آرگومان به سازنده وکتورایزر به عدد کوچکتری محدود کنیم:\n",
|
||
"\n",
|
||
"هر دوی این مراحل را میتوان با استفاده از لایه **TextVectorization** مدیریت کرد. بیایید شیء وکتورایزر را ایجاد کنیم و سپس با فراخوانی متد `adapt` تمام متن را مرور کرده و یک واژگان بسازیم:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 5,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"vocab_size = 50000\n",
|
||
"vectorizer = keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size)\n",
|
||
"vectorizer.adapt(ds_train.take(500).map(lambda x: x['title']+' '+x['description']))"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"> **توجه** ما تنها از یک زیرمجموعه از کل دادهها برای ساخت واژگان استفاده میکنیم. این کار را برای افزایش سرعت اجرا و جلوگیری از انتظار شما انجام میدهیم. با این حال، این خطر وجود دارد که برخی از کلمات موجود در کل مجموعه دادهها وارد واژگان نشوند و در طول آموزش نادیده گرفته شوند. بنابراین، استفاده از اندازه کامل واژگان و اجرای آن بر روی کل مجموعه داده در طول `adapt` باید دقت نهایی را افزایش دهد، اما نه به طور قابل توجهی.\n",
|
||
"\n",
|
||
"اکنون میتوانیم به واژگان واقعی دسترسی داشته باشیم:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 6,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"['', '[UNK]', 'the', 'to', 'a', 'in', 'of', 'and', 'on', 'for']\n",
|
||
"Length of vocabulary: 5335\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"vocab = vectorizer.get_vocabulary()\n",
|
||
"vocab_size = len(vocab)\n",
|
||
"print(vocab[:10])\n",
|
||
"print(f\"Length of vocabulary: {vocab_size}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"با استفاده از وکتورایزر، میتوانیم بهراحتی هر متنی را به مجموعهای از اعداد کدگذاری کنیم:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 7,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"<tf.Tensor: shape=(7,), dtype=int64, numpy=array([ 112, 3695, 3, 304, 11, 1041, 1], dtype=int64)>"
|
||
]
|
||
},
|
||
"execution_count": 7,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"vectorizer('I love to play with my words')"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## نمایش متن به روش کیسه کلمات\n",
|
||
"\n",
|
||
"از آنجا که کلمات حامل معنا هستند، گاهی میتوانیم با نگاه کردن به کلمات جداگانه، بدون توجه به ترتیب آنها در جمله، معنای یک متن را درک کنیم. برای مثال، هنگام دستهبندی اخبار، کلماتی مانند *آبوهوا* و *برف* احتمالاً به *پیشبینی آبوهوا* اشاره دارند، در حالی که کلماتی مانند *سهام* و *دلار* به *اخبار مالی* مربوط میشوند.\n",
|
||
"\n",
|
||
"نمایش برداری **کیسه کلمات** (Bag-of-words یا BoW) سادهترین روش سنتی برای نمایش برداری است که میتوان آن را درک کرد. در این روش، هر کلمه به یک شاخص بردار مرتبط میشود و هر عنصر بردار تعداد دفعات وقوع هر کلمه در یک سند مشخص را نشان میدهد.\n",
|
||
"\n",
|
||
"\n",
|
||
"\n",
|
||
"> **Note**: میتوانید BoW را بهعنوان مجموع تمام بردارهای کدگذاریشده به روش یکداغ (one-hot-encoded) برای کلمات جداگانه در متن نیز در نظر بگیرید.\n",
|
||
"\n",
|
||
"در زیر یک مثال از نحوه تولید نمایش کیسه کلمات با استفاده از کتابخانه Scikit Learn در پایتون آورده شده است:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 8,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"array([[1, 1, 0, 2, 0, 0, 0, 0, 0]], dtype=int64)"
|
||
]
|
||
},
|
||
"execution_count": 8,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"from sklearn.feature_extraction.text import CountVectorizer\n",
|
||
"sc_vectorizer = CountVectorizer()\n",
|
||
"corpus = [\n",
|
||
" 'I like hot dogs.',\n",
|
||
" 'The dog ran fast.',\n",
|
||
" 'Its hot outside.',\n",
|
||
" ]\n",
|
||
"sc_vectorizer.fit_transform(corpus)\n",
|
||
"sc_vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"ما همچنین میتوانیم از بردارساز Keras که در بالا تعریف کردیم استفاده کنیم، هر شماره کلمه را به یک کدگذاری یکداغ تبدیل کرده و همه آن بردارها را جمع کنیم:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 9,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"array([0., 5., 0., ..., 0., 0., 0.], dtype=float32)"
|
||
]
|
||
},
|
||
"execution_count": 9,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"def to_bow(text):\n",
|
||
" return tf.reduce_sum(tf.one_hot(vectorizer(text),vocab_size),axis=0)\n",
|
||
"\n",
|
||
"to_bow('My dog likes hot dogs on a hot day.').numpy()"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"> **توجه**: ممکن است تعجب کنید که نتیجه با مثال قبلی متفاوت است. دلیل این تفاوت این است که در مثال Keras طول بردار با اندازه واژگان مطابقت دارد، که از کل مجموعه داده AG News ساخته شده بود، در حالی که در مثال Scikit Learn ما واژگان را به صورت لحظهای از متن نمونه ساختیم.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## آموزش دستهبند BoW\n",
|
||
"\n",
|
||
"حالا که یاد گرفتیم چگونه نمایش کیسهای از کلمات (bag-of-words) متن خود را بسازیم، بیایید یک دستهبند که از این نمایش استفاده میکند را آموزش دهیم. ابتدا باید مجموعه داده خود را به نمایش کیسهای از کلمات تبدیل کنیم. این کار را میتوان با استفاده از تابع `map` به شکل زیر انجام داد:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 11,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"batch_size = 128\n",
|
||
"\n",
|
||
"ds_train_bow = ds_train.map(lambda x: (to_bow(x['title']+x['description']),x['label'])).batch(batch_size)\n",
|
||
"ds_test_bow = ds_test.map(lambda x: (to_bow(x['title']+x['description']),x['label'])).batch(batch_size)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"حالا بیایید یک شبکه عصبی طبقهبندی ساده تعریف کنیم که شامل یک لایه خطی است. اندازه ورودی `vocab_size` است و اندازه خروجی مربوط به تعداد کلاسها (۴) میباشد. چون ما در حال حل یک وظیفه طبقهبندی هستیم، تابع فعالسازی نهایی **softmax** است:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 12,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"938/938 [==============================] - 66s 70ms/step - loss: 0.6144 - acc: 0.8427 - val_loss: 0.4416 - val_acc: 0.8697\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"<keras.callbacks.History at 0x20c70a947f0>"
|
||
]
|
||
},
|
||
"execution_count": 12,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"model = keras.models.Sequential([\n",
|
||
" keras.layers.Dense(4,activation='softmax',input_shape=(vocab_size,))\n",
|
||
"])\n",
|
||
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
|
||
"model.fit(ds_train_bow,validation_data=ds_test_bow)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"از آنجایی که ما ۴ کلاس داریم، دقت بالای ۸۰٪ نتیجهی خوبی محسوب میشود.\n",
|
||
"\n",
|
||
"## آموزش یک طبقهبند بهعنوان یک شبکه\n",
|
||
"\n",
|
||
"از آنجا که وکتورایزر نیز یک لایهی Keras است، میتوانیم شبکهای تعریف کنیم که آن را شامل شود و بهصورت انتها به انتها آموزش دهیم. به این ترتیب نیازی به وکتورایز کردن دیتاست با استفاده از `map` نداریم و میتوانیم دیتاست اصلی را مستقیماً به ورودی شبکه بدهیم.\n",
|
||
"\n",
|
||
"> **توجه**: همچنان باید از map برای تبدیل فیلدها از دیکشنریها (مانند `title`، `description` و `label`) به تاپلها استفاده کنیم. با این حال، هنگام بارگذاری دادهها از دیسک، میتوانیم از همان ابتدا دیتاستی با ساختار مورد نیاز بسازیم.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 13,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Model: \"model\"\n",
|
||
"_________________________________________________________________\n",
|
||
" Layer (type) Output Shape Param # \n",
|
||
"=================================================================\n",
|
||
" input_1 (InputLayer) [(None, 1)] 0 \n",
|
||
" \n",
|
||
" text_vectorization (TextVec (None, None) 0 \n",
|
||
" torization) \n",
|
||
" \n",
|
||
" tf.one_hot (TFOpLambda) (None, None, 5335) 0 \n",
|
||
" \n",
|
||
" tf.math.reduce_sum (TFOpLam (None, 5335) 0 \n",
|
||
" bda) \n",
|
||
" \n",
|
||
" dense_2 (Dense) (None, 4) 21344 \n",
|
||
" \n",
|
||
"=================================================================\n",
|
||
"Total params: 21,344\n",
|
||
"Trainable params: 21,344\n",
|
||
"Non-trainable params: 0\n",
|
||
"_________________________________________________________________\n",
|
||
"938/938 [==============================] - 73s 77ms/step - loss: 0.6057 - acc: 0.8414 - val_loss: 0.4202 - val_acc: 0.8736\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"<keras.callbacks.History at 0x20c721521f0>"
|
||
]
|
||
},
|
||
"execution_count": 13,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"def extract_text(x):\n",
|
||
" return x['title']+' '+x['description']\n",
|
||
"\n",
|
||
"def tupelize(x):\n",
|
||
" return (extract_text(x),x['label'])\n",
|
||
"\n",
|
||
"inp = keras.Input(shape=(1,),dtype=tf.string)\n",
|
||
"x = vectorizer(inp)\n",
|
||
"x = tf.reduce_sum(tf.one_hot(x,vocab_size),axis=1)\n",
|
||
"out = keras.layers.Dense(4,activation='softmax')(x)\n",
|
||
"model = keras.models.Model(inp,out)\n",
|
||
"model.summary()\n",
|
||
"\n",
|
||
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
|
||
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## بیگرامها، تریگرامها و اِنگرامها\n",
|
||
"\n",
|
||
"یکی از محدودیتهای روش کیسه کلمات این است که برخی کلمات بخشی از عبارات چندکلمهای هستند. برای مثال، کلمه «هات داگ» معنای کاملاً متفاوتی نسبت به کلمات «هات» و «داگ» در سایر زمینهها دارد. اگر همیشه کلمات «هات» و «داگ» را با استفاده از بردارهای یکسان نمایش دهیم، ممکن است مدل ما را دچار سردرگمی کند.\n",
|
||
"\n",
|
||
"برای حل این مشکل، **نمایشهای اِنگرام** اغلب در روشهای طبقهبندی اسناد استفاده میشوند، جایی که فراوانی هر کلمه، دوکلمهای یا سهکلمهای ویژگی مفیدی برای آموزش طبقهبندها است. در نمایش بیگرام، برای مثال، علاوه بر کلمات اصلی، تمام جفت کلمات را نیز به واژگان اضافه میکنیم.\n",
|
||
"\n",
|
||
"در زیر مثالی از نحوه تولید نمایش کیسه کلمات بیگرام با استفاده از Scikit Learn آورده شده است:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 14,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Vocabulary:\n",
|
||
" {'i': 7, 'like': 11, 'hot': 4, 'dogs': 2, 'i like': 8, 'like hot': 12, 'hot dogs': 5, 'the': 16, 'dog': 0, 'ran': 14, 'fast': 3, 'the dog': 17, 'dog ran': 1, 'ran fast': 15, 'its': 9, 'outside': 13, 'its hot': 10, 'hot outside': 6}\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"array([[1, 0, 1, 0, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],\n",
|
||
" dtype=int64)"
|
||
]
|
||
},
|
||
"execution_count": 14,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"bigram_vectorizer = CountVectorizer(ngram_range=(1, 2), token_pattern=r'\\b\\w+\\b', min_df=1)\n",
|
||
"corpus = [\n",
|
||
" 'I like hot dogs.',\n",
|
||
" 'The dog ran fast.',\n",
|
||
" 'Its hot outside.',\n",
|
||
" ]\n",
|
||
"bigram_vectorizer.fit_transform(corpus)\n",
|
||
"print(\"Vocabulary:\\n\",bigram_vectorizer.vocabulary_)\n",
|
||
"bigram_vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"عیب اصلی روش n-gram این است که اندازه واژگان به سرعت بسیار زیاد رشد میکند. در عمل، لازم است که نمایش n-gram را با یک تکنیک کاهش ابعاد، مانند *تعبیهها*، ترکیب کنیم که در واحد بعدی درباره آن صحبت خواهیم کرد.\n",
|
||
"\n",
|
||
"برای استفاده از نمایش n-gram در مجموعه داده **AG News**، باید پارامتر `ngrams` را به سازنده `TextVectorization` خود منتقل کنیم. طول واژگان بیگرام **به طور قابل توجهی بزرگتر** است، در مورد ما بیش از 1.3 میلیون توکن! بنابراین منطقی است که تعداد توکنهای بیگرام را نیز به یک عدد معقول محدود کنیم.\n",
|
||
"\n",
|
||
"میتوانیم از همان کدی که در بالا استفاده کردیم برای آموزش طبقهبند استفاده کنیم، اما این کار از نظر حافظه بسیار ناکارآمد خواهد بود. در واحد بعدی، طبقهبند بیگرام را با استفاده از تعبیهها آموزش خواهیم داد. در همین حال، میتوانید با آموزش طبقهبند بیگرام در این دفترچه آزمایش کنید و ببینید آیا میتوانید دقت بالاتری به دست آورید.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## محاسبه خودکار بردارهای BoW\n",
|
||
"\n",
|
||
"در مثال بالا، بردارهای BoW را به صورت دستی با جمع کردن کدگذاریهای یکداغ کلمات جداگانه محاسبه کردیم. با این حال، نسخه جدید TensorFlow به ما این امکان را میدهد که با استفاده از پارامتر `output_mode='count` در سازنده وکتورایزر، بردارهای BoW را به صورت خودکار محاسبه کنیم. این کار تعریف و آموزش مدل ما را به طور قابل توجهی سادهتر میکند:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 15,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Training vectorizer\n",
|
||
"938/938 [==============================] - 7s 7ms/step - loss: 0.5929 - acc: 0.8486 - val_loss: 0.4168 - val_acc: 0.8772\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"<keras.callbacks.History at 0x20c725217c0>"
|
||
]
|
||
},
|
||
"execution_count": 15,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"model = keras.models.Sequential([\n",
|
||
" keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size,output_mode='count'),\n",
|
||
" keras.layers.Dense(4,input_shape=(vocab_size,), activation='softmax')\n",
|
||
"])\n",
|
||
"print(\"Training vectorizer\")\n",
|
||
"model.layers[0].adapt(ds_train.take(500).map(extract_text))\n",
|
||
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
|
||
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## فراوانی واژه - فراوانی معکوس سند (TF-IDF)\n",
|
||
"\n",
|
||
"در نمایش BoW، وقوع واژهها با استفاده از یک تکنیک یکسان وزندهی میشود، بدون توجه به خود واژه. با این حال، واضح است که واژههای پرتکراری مانند *a* و *in* برای طبقهبندی بسیار کماهمیتتر از اصطلاحات تخصصی هستند. در بیشتر وظایف پردازش زبان طبیعی (NLP)، برخی واژهها نسبت به دیگران مرتبطتر هستند.\n",
|
||
"\n",
|
||
"**TF-IDF** مخفف **فراوانی واژه - فراوانی معکوس سند** است. این یک نوع تغییر یافته از مدل کیسه واژهها (bag-of-words) است که به جای استفاده از مقدار دودویی ۰/۱ برای نشان دادن حضور یک واژه در یک سند، از یک مقدار اعشاری استفاده میکند که به فراوانی وقوع واژه در مجموعه متون مرتبط است.\n",
|
||
"\n",
|
||
"به طور رسمیتر، وزن $w_{ij}$ یک واژه $i$ در سند $j$ به صورت زیر تعریف میشود:\n",
|
||
"$$\n",
|
||
"w_{ij} = tf_{ij}\\times\\log({N\\over df_i})\n",
|
||
"$$\n",
|
||
"که در آن:\n",
|
||
"* $tf_{ij}$ تعداد وقوعهای $i$ در $j$ است، یعنی همان مقدار BoW که قبلاً دیدهایم\n",
|
||
"* $N$ تعداد کل اسناد در مجموعه است\n",
|
||
"* $df_i$ تعداد اسنادی است که واژه $i$ را در کل مجموعه شامل میشوند\n",
|
||
"\n",
|
||
"مقدار TF-IDF یعنی $w_{ij}$ به طور متناسب با تعداد دفعاتی که یک واژه در یک سند ظاهر میشود افزایش مییابد و با تعداد اسناد در مجموعه که آن واژه را شامل میشوند تنظیم میشود. این امر کمک میکند تا برای این واقعیت که برخی واژهها بیشتر از دیگران ظاهر میشوند، تعدیل صورت گیرد. برای مثال، اگر یک واژه در *تمامی* اسناد مجموعه ظاهر شود، $df_i=N$ و $w_{ij}=0$ خواهد بود و آن واژهها به طور کامل نادیده گرفته میشوند.\n",
|
||
"\n",
|
||
"شما میتوانید به راحتی بردارسازی TF-IDF متن را با استفاده از Scikit Learn ایجاد کنید:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 16,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"array([[0.43381609, 0. , 0.43381609, 0. , 0.65985664,\n",
|
||
" 0.43381609, 0. , 0. , 0. , 0. ,\n",
|
||
" 0. , 0. , 0. , 0. , 0. ,\n",
|
||
" 0. ]])"
|
||
]
|
||
},
|
||
"execution_count": 16,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"from sklearn.feature_extraction.text import TfidfVectorizer\n",
|
||
"vectorizer = TfidfVectorizer(ngram_range=(1,2))\n",
|
||
"vectorizer.fit_transform(corpus)\n",
|
||
"vectorizer.transform(['My dog likes hot dogs on a hot day.']).toarray()"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"در کراس، لایه `TextVectorization` میتواند با استفاده از پارامتر `output_mode='tf-idf'` به طور خودکار فراوانیهای TF-IDF را محاسبه کند. بیایید کدی که در بالا استفاده کردیم را تکرار کنیم تا ببینیم آیا استفاده از TF-IDF دقت را افزایش میدهد:\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 17,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Training vectorizer\n",
|
||
"938/938 [==============================] - 12s 12ms/step - loss: 0.4197 - acc: 0.8662 - val_loss: 0.3432 - val_acc: 0.8849\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"<keras.callbacks.History at 0x20c729dfd30>"
|
||
]
|
||
},
|
||
"execution_count": 17,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"model = keras.models.Sequential([\n",
|
||
" keras.layers.experimental.preprocessing.TextVectorization(max_tokens=vocab_size,output_mode='tf-idf'),\n",
|
||
" keras.layers.Dense(4,input_shape=(vocab_size,), activation='softmax')\n",
|
||
"])\n",
|
||
"print(\"Training vectorizer\")\n",
|
||
"model.layers[0].adapt(ds_train.take(500).map(extract_text))\n",
|
||
"model.compile(loss='sparse_categorical_crossentropy',optimizer='adam',metrics=['acc'])\n",
|
||
"model.fit(ds_train.map(tupelize).batch(batch_size),validation_data=ds_test.map(tupelize).batch(batch_size))"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## نتیجهگیری\n",
|
||
"\n",
|
||
"با اینکه نمایشهای TF-IDF وزنهای فرکانسی را به کلمات مختلف اختصاص میدهند، اما قادر به نمایش معنا یا ترتیب نیستند. همانطور که زبانشناس مشهور جی. آر. فرث در سال 1935 گفت: \"معنای کامل یک کلمه همیشه وابسته به متن است و هیچ مطالعهای درباره معنا بدون در نظر گرفتن متن نمیتواند جدی گرفته شود.\" در ادامه دوره، یاد خواهیم گرفت که چگونه اطلاعات متنی را با استفاده از مدلسازی زبان استخراج کنیم.\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"\n---\n\n**سلب مسئولیت**: \nاین سند با استفاده از سرویس ترجمه هوش مصنوعی [Co-op Translator](https://github.com/Azure/co-op-translator) ترجمه شده است. در حالی که ما تلاش میکنیم دقت را حفظ کنیم، لطفاً توجه داشته باشید که ترجمههای خودکار ممکن است شامل خطاها یا نادرستیها باشند. سند اصلی به زبان اصلی آن باید به عنوان منبع معتبر در نظر گرفته شود. برای اطلاعات حساس، توصیه میشود از ترجمه حرفهای انسانی استفاده کنید. ما مسئولیتی در قبال سوء تفاهمها یا تفسیرهای نادرست ناشی از استفاده از این ترجمه نداریم.\n"
|
||
]
|
||
}
|
||
],
|
||
"metadata": {
|
||
"interpreter": {
|
||
"hash": "0cb620c6d4b9f7a635928804c26cf22403d89d98d79684e4529119355ee6d5a5"
|
||
},
|
||
"kernel_info": {
|
||
"name": "conda-env-py37_tensorflow-py"
|
||
},
|
||
"kernelspec": {
|
||
"display_name": "py37_tensorflow",
|
||
"language": "python",
|
||
"name": "python3"
|
||
},
|
||
"language_info": {
|
||
"codemirror_mode": {
|
||
"name": "ipython",
|
||
"version": 3
|
||
},
|
||
"file_extension": ".py",
|
||
"mimetype": "text/x-python",
|
||
"name": "python",
|
||
"nbconvert_exporter": "python",
|
||
"pygments_lexer": "ipython3",
|
||
"version": "3.8.12"
|
||
},
|
||
"nteract": {
|
||
"version": "nteract-front-end@1.0.0"
|
||
},
|
||
"coopTranslator": {
|
||
"original_hash": "19b43951d55b377a76209c24c1f017e4",
|
||
"translation_date": "2025-08-31T17:20:24+00:00",
|
||
"source_file": "lessons/5-NLP/13-TextRep/TextRepresentationTF.ipynb",
|
||
"language_code": "fa"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 4
|
||
} |