Convolutional Neural Networks for Sentence Classification

تغییرات پروژه از تاریخ 1396/10/09 تا تاریخ 1396/11/21
## ** دسته بندی جملات با استفاده از شبکه های عصبی کانولوشن**

**********

##  ** بخش اول : مفاهیم نظری مقاله**

### **1. مقدمه**
دسته­ بندی متن [^Text Classification] را می­ توان یکی از بخش های مهم پردازش زبان طبیعی دانست. این دسته بندی می تواند در زمینه های مختلفی انجام شود. مثلا تعیین عنوان یا ژانر مناسب برای متن، تشخیص هرزنامه [^Spam] از بین ایمیل ها، تشخیص جنسیت نویسنده، تحلیل مثبت و منفی بودن یک نظر یا توییت، تعیین فیلد یک مقاله و بسیاری کاربردهای دیگر. 
کلیت عملکرد این نوع روش ها به این ترتیب است که تعداد m متن و j کلاس داریم. به هر متن به صورت دستی یک برچسب کلاس نسبت داده شده است.  به گونه ای که تمام المان های دیتاست، زوج های $ \left( { d }_{ i },{ c }_{ i } \right)  $  هستند که $ { d }_{ i } $ متن i ام و $ { c }_{ i } $ برچسب کلاس متناظر با آن است. هدف ما این است که بعد از اتمام مرحله آموزش با دریافت $ { d }_{ x } $ جدید، مدل بتواند برچسب متناسب با آن را تشخیص دهد.
برای آموزش چنین مدلی می ­توانیم از هر یک از دسته­ بندهای کلاسیک از جمله بیز، ماشین بردار پشتیبان [^Support Vector Machine (SVM)]، شبکه ­های عصبی مصنوعی و ... استفاده کنیم. با گسترش یادگیری عمیق و به کارگیری آن در حوزه های مختلف، این روش در مسائل دسته بندی متن هم مورد استفاده قرار گرفته است  [1][2]
در ادامه این گزارش به تعریف مسئله می پردازیم، در بخش سوم و چهارم مروری بر ادبیات و کارهای پیشین خواهیم داشت و برخی مفاهیم بنیادین را بررسی می کنیم. در بخش پنجم معماری پیشنهادی را شرح می دهیم و در بخش ششم جزییات دیتاست های به کار رفته در این پژوهش را بررسی می کنیم. دو بخش بعدی مربوط به آموزش و پیاده سازی مدل است و نهایتا با ارائه نتایج به دست آمده به این گزارش خاتمه می دهیم.


----------


### **2. تعریف مسئله**
دسته بندی جمله  [^Sentence Classification] مشابه دسته بندی متن است که همان طور که پیش تر بیان شد در آن به هر جمله ، یک برچسب کلاس تعلق می گیرد. دسته بندی جملات می تواند بر اساس نوع جمله باشد و به کلاس های خبری، پرسشی، تعجبی و امری تقسیم بندی شود و یا بر اساس مفهوم و با توجه به مسئله در کلاس های متفاوتی دسته بندی شوند. مثلا نظرات کاربران در مورد یک محصول را به دو گروه منفی و مثبت تفکیک کند.
مقاله حاضر بر دسته بندی مفهومی جملات تمرکز کرده است. در مدل به کار رفته با استفاده از بردارهای واژگان از پیش آموزش دیده word2vec  و همچنین شبکه های عصبی کانولوشن نتایج بسیار خوبی روی هفت مجموعه دادگان مختلف به دست آمده است. در بخش های بعد توضیحات مفصل تری در این زمینه خواهیم داد.


----------


### **3. مرور ادبیات و معرفی کارهای مرتبط پیشین**
مدل های یادگیری عمیق در سال های اخیر توانسته اند نتایج بسیار درخشانی در حوزه تصویر و صوت کسب کنند. همچنین پژوهشگران در زمینه پردازش زبان طبیعی و بر روی بردار واژگان تحقیقات گسترده ای انجام داده اند. عملکرد بردار واژگان به این ترتیب است که با استفاده از روش های شبکه عصبی به ازای هر کلمه برداری به دست می آید که ابعاد آن بسیار کمتر از بردارV بعدی کلمه است.(V تعداد کلمات دیکشنری است) در چنین بازنمایشی کلماتی که از نظر مفهومی به هم نزدیکند در فضای برداری هم به هم نزدیک خواهند بود. نتایج این بردارهای واژگان مثل مجموعه [word2vec](https://code.google.com/p/word2vec/) و [GloVe](https://nlp.stanford.edu/projects/glove/) به صورت از پیش آموزش دیده [^Pretrained] در دسترس است. [3][4]
از طرفی طبق پژوهش های اخیر، شبکه های عصبی کانولوشن که عمدتا در حوزه تصویر به کار می روند، می توانند در پردازش زبان هم به کار گرفته شوند و در بهبود نتایج موثر باشند. عملکرد این شبکه ها به این صورت است که با تعریف یک ماتریس به عنوان فیلتر و جابه جایی آن در سطح تصویر یا متن، ویژگی های محلی[^local feature] متفاوتی استخراج می شود که می تواند درمدل سازی جمله[^Sentence Modeling] تجزیه معنایی[^Semantic Parsing] و بازیابی پرس و جو [^Query Retrieval] مورد استفاده قرار گیرد. [5][6][7]
با استفاده از این دو روش، یعنی بردارهای واژگان از پیش آموزش دیده و شبکه های عصبی کانولوشن، در این مقاله مدلی بر مبنای CNN و با استفاده از بردار واژگان word2vec آموزش داده شده است که دقت های به دست آمده از آن در مسئله دسته بندی متن از روش های قبلی بالاتر است.


----------


### **4. مفاهیم اولیه پیش نیاز **
برای درک بهتر ساختار و ایده مقاله حاضر، ابتدا لازم است با چند مفهوم مهم که در این پژوهش به کار رفته، آشنا شویم. در ادامه مباحث convolution ، pooling ، dropout ، softmax و کانال را که در بخش معماری استفاده شده اند به طور مختصر بررسی خواهیم کرد. 
+ **  ًconvolution **
ساده ترین روش برای بیان مفهوم کانولوشن یک پنجره لغزان است که به صورت یک ماتریس نمایش داده می شود. به این ماتریس کرنل [^Kernel]، فیلتر یا استخراج گر ویژگی[^Feature extractor] می گوییم. هر درایه کرنل یک عدد(ضریب) است و با جا به جایی کرنل، مقادیر ورودی، در ضریب متناظرشان در کرنل ضرب می شوند و حاصل جمع آنها به عنوان نتیجه در پاسخ درج می شود. [8]
از کانولوشن میتوان برای شناسایی حاشیه در تصاویر یا فیلترهای مختلف دیگر استفاده کرد.CNN مجموعه ای از چند لایه کانولوشن است که بر روی خروجی آنها توابع فعالساز[^Activation Function] غیرخطی مثل ReLU و tanh اعمال می شود. فرمول زیر چگونگی عملکرد کانولوشن(K) بر روی ماتریس نمونه ورودی(I) را نشان می دهد. $h$ و $d$  و$w$ به ترتیب ارتفاع، عمق و عرض فیلتر هستند.
$$\mathrm{conv}(I, K)_{xy} = \sigma\left(b + \sum_{i=1}^h \sum_{j=1}^w \sum_{k=1}^d {K_{ijk} \cdot I_{x + i - 1, y + j - 1, k}}\right)$$
![شکل1:چگونگی عملکرد کانولوشن بر روی ماتریس ورودی](https://boute.s3.amazonaws.com/300-Convolution_schematic.gif)
+ **  ًpooling **
لایه Pooling که معمولا بعد از لایه های پیچشی قرار میگیرد وظیفه نمونه برداری[^subsampling] از داده های ورودی را دارد. این کار می تواند به روش های مختلفی انجام شود که معمول ترین آنها استفاده از عملگر ماکسیمم است. فیلتری با اندازه مشخص روی ورودی اعمال می شود. با جا به جایی فیلتر در هر پنجره، بزرگ ترین عدد انتخاب می شود. ماتریسی که در نهایت به عنوان خروجی ارائه می شود شامل بزرگترین داده ها در هر قاب است. تصویر زیر به خوبی عملکرد max-pooling و average-pooling را نشان می دهد. نکته مهمی که باید به آن توجه شود این است که جا به جایی فیلتر به گونه ای انجام می شود که هم پوشانی اتفاق نیفتد. [9]
![شکل2:چگونگی عملکرد دو نوع pooling که بیش از بقیه کاربرد دارند: max-pooling و average-pooling](https://boute.s3.amazonaws.com/300-1_C0EwU0aknuliOsGktK6U0g.png)
+ **  ًdropout**
شبکه های عصبی کاملا متصل[^fully-connected] می توانند دچار بیش برازش[^overfit] شوند. برای جلوگیری از این مشکل، معمولا بعد از لایه های کاملا متصل یک لایه dropout قرار می دهند. عملکرد این لایه ها به این ترتیب است که به صورت تصادفی و با احتمال $ p $ خروجی برخی نورون ها را در محاسبات نادیده می گیرند. این ایده توسط آقای هینتون ارائه شد و در عمل باعث بهبود چشمگیر نتایج شد. دانستن همین کلیات از dropout برای این مقاله کافی است فلذا از بیان جزییات بیشتر صرف نظر می کنیم. [10]
تصویر 3 که از مقاله اصلی گرفته شده است، عملکرد این تابع را به خوبی نشان می دهد.
![شکل3:چگونگی عملکرد dropout بر روی چند لایه کاملا-متصل](https://boute.s3.amazonaws.com/300-3.jpg)
+ **  ًsoftmax**
تابع softmax یک بردار $k$ بعدی مثل $z$ ، که شامل اعداد حقیقی است، را به عنوان ورودی می گیرد و بردار $k$ بعدی $ \sigma (z)$  که شامل اعداد در بازه $[0,1]$ است را به عنوان خروجی می دهد به گونه ای که حاصل جمع این اعداد دقیقا 1 شود. به عبارت دیگر این تابع اعداد ورودی را به مقادیر احتمال آنها نگاشت می کند. نمایش ریاضی این عمل به صورت فرمول زیر است:
$$  \sigma (z)_{j} =   \frac{e^{ z_{j} } }{ \sum_k^K e^{ z_{k} } }  $$که در آن $j=1,2,...,K$ است. توجه کنید که مخرج کسر صرفا برای نرمال سازی اعداد است. این تابع برخلاف ظاهر پیچیده، کارکرد بسیار ساده ای دارد که با ارائه مثالی ، شفاف تر خواهد شد. فرض کنید ورودی ما برداری با مقادیر $[1, -2, 0] $ است و مسئله ماکلاس بندی هر داده در یکی از سه کلاس ماشین، موتور و هواپیماست. با اعمال تابع softmax بر این ورودی، مقادیر $ [0.7, 0.04, 0.26]$ حاصل می شود که در نتیجه آن، به داده ورودی برچسب «ماشین» تخصیص می یابد.[11][12]
$$[1, -2, 0] \rightarrow [e^1, e^{-2}, e^0] = [2.71, 0.14, 1] \rightarrow [0.7, 0.04, 0.26]$$
 
+ **  کانال**
یکی دیگر از مفاهیمی که لازم است با آن آشنا شویم، کانال است. کانال ها در حقیقت "دید"[^view] های مختلف از داده ورودی واحد هستند. به عنوان مثال در حوزه تصویر عموما کانال های RGB (قرمز، سبز، آبی) داریم. می توان فیلترهای کانولوشن را با وزن های یکسان یا متفاوت بر کانال های مختلف اعمال کرد. اما این کانال ها در پردازش زبان طبیعی چه مفهومی دارند؟ می توانیم برای تعبیه متن های متفاوت، کانال های متفاوت داشته باشیم(مثلا word2vec و GloVe) و یا اینکه یک جمله، به زبان های مختلف وجود داشته باشد و هر زبان یک کانال باشد.[8]


+ **  جمع بندی و انتقال این مفاهیم به حوزه پردازش زبان طبیعی**
سوالی که همچنان باقی مانده است، این است که تمام این مفاهیم چگونه در حوزه پردازش زبان طبیعی به کار می روند؟ پاسخ ساده است. ورودی، به جای پیکسل های تصاویر، جملات یا متونی هستند که به صورت یک ماتریس نمایش داده می شوند. هر سطر ماتریس نمایانگر یک توکن(عموما یک واژه) است. به عبارت دیگر هر سطر یک بردار است که مختص یک واژه است. این بردار میتواند بازنمایش one-hot کلمه، یا بازنمایش تعبیه متن با ابعاد کمتر مثل word2vec یا  GloVe باشد. پس برای یک جمله ده کلمه ای با تعبیه متن 100 بعدی، ماتریسی به ابعاد 100×10 به عنوان ورودی خواهیم داشت.
تصویر زیر مجموعه این مفاهیم مطرح شده را به خوبی نشان می دهد. ماتریس سفید جمله ورودی ما است که  یک جمله 7 کلمه ای با ابعاد تعبیه متن 5 است. بر روی این ورودی دو فیلتر با طول 4، دو فیلتر با طول 3 و دو فیلتر با طول 2 اعمال می شود. نتیجه حاصل از هر یک از فیلترها به صورت یک بردار در مرحله بعد نشان داده شده است. به این بردار نقشه ویژگی[^Feature map] می گویند. در مرحله بعد maxpooling روی هر نقشه ویژگی اعمال می شود و بزرگترین مقدار بردار را برمی گرداند و نتایج آنها به هم متصل می شود و یک بردار به طول شش حاصل می شود. در نهایت یک تابع softmax روی کل بردار اعمال می شود و نتیجه کلاس بندی را به ما می دهد.[8][13]

![معماری یک شبکه دسته بندی جمله. این مدل شامل لایه های پیچشی، pooling ، softmax و dropout است](https://boute.s3.amazonaws.com/300-Screen-Shot-2015-11-06-at-12.05.40-PM.png)


----------


### **5. معماری شبکه**
با دانستن مفاهیم بخش قبل، معرفی معماری شبکه بسیار ساده خواهد بود. ورودی مدل، به ازای هر جمله ی $ n $ کلمه ای، یک ماتریس $n * k$ است که $ k $ ابعاد تعبیه متن به کار رفته است. این ماتریس از پشت سر هم قرار گرفتن[^concatenate] بردار کلمات جمله یعنی ${ x }_{ i }$ های 1 تا n ساخته شده است که به صورت زیر نمایش داده می شود:
$$  x_{1:n} =  x_{1}  \bigoplus x_{2} \bigoplus ... \bigoplus  x_{n} $$
بر روی این لایه ورودی یک لایه پیچشی اعمال می شود که شامل فیلترهایی مثل $w$  با اندازه $h * k$ است. دقت کنید که طول فیلترها با اندازه تعبیه متن به کار رفته برابر است و تفاوت آنها در $h$ (ارتفاع فیلترها) است. پس این فیلترها تنها در یک جهت بر روی ماتریس ورودی جا به جا می شوند. خروجی حاصل از اعمال این فیلترها، i ویژگی است که با فرمول زیر محاسبه می شوند:
$$ c_{i} =  f(w. x_{i:i+h-1} + b)$$
که در حقیقت $f$ یک تابع فعال ساز غیر خطی است. با اعمال فیلتر ها به همه قاب های ممکن نقشه ی ویژگی  به صورت زیر به دست می آید:
$$c =  [ c_{1}, c_{2}, ... , c_{n-h+1}]$$
لایه بعد یک لایه max-pooling است که با اعمال بر روی هر نقشه ویژگی، بزرگترین مقدار آن بردار را انتخاب می کند. به عبارت دیگر خروجی این تابع $\hat{c} = max \big\{c\big\} $ است. در نهایت بردار حاصل از این مرحله به یک لایه کاملا متصل وارد می شود و dropout و softmax روی آن اعمال می شود تا خروجی مدل یک توزیع احتمال معتبر باشد.
شکل 4 معماری مدل را به صورت نمادین به تصویر کشیده است. در بخش بعد در مورد مقادیر پارامترهای مدل، نظیر ابعاد تعبیه متن و اندازه و تعداد فیلترها توضیحاتی خواهیم داد.
![شکل 4: معماری دو کاناله مورد استفاده برای دسته بندی جملات](https://boute.s3.amazonaws.com/300-Screen-Shot-2015-11-06-at-8.03.47-AM.jpg)
پژوهشگر، چهار گونه متفاوت از معماری اصلی را معرفی کرده، که کلیات همه مشترک است و در جزییات با هم تفاوت هایی دارند. هر یک از این چهار نوع مدل بر روی همه مجموعه داده ها آموزش داده شده اند و نتایج بررسی شده است. این چهار مدل عبارتند از:
+ **ًCNN-rand:** معماری پایه + مقداردهی اولیه تصادفی به تمامی کلمات
+ **ًCNN-static:** معماری پایه + مقداردهی اولیه بردار کلمات به وسیله word2vec  + در تمام مرحله آموزش این مقادیر ثابت اند و سایر پارامترهای مدل آپدیت می شوند.
+ **ًCNN-non-static:** مشابه مدل بالا + تمام مقادیر آپدیت می شوند.
+ **ًCNN-multichannel:** معماری پایه + استفاده از دو کانال مجزا + مقداردهی اولیه بردار کلمات هر دو کانال به وسیله word2vec  + در تمام مرحله آموزش فقط مقادیر یکی از کانال ها آپدیت می شود و دیگری ثابت است.


----------


### **6. معرفی دادگان**
مدل معرفی شده در این مقاله بر روی هفت دادگان مهم اعمال شده و نتایج به دست آمده به طور خلاصه در جدول 1 قابل مشاهده است. مجموعه های دادگان مورد استفاده عبارتند از:
+ **نظرات در مورد فیلم ها ([MR](http://www.cs.cornell.edu/people/pabo/movie-review-data/)) :** این مجموعه دادگان شامل 10662 نظر در مورد فیلم ها است که در دو کلاس مثبت و منفی دسته بندی شده اند(به طور تقریبا مساوی) این دادگان در دسترس عموم است.
+ **درخت مفهومی استنفرد 1 ([SST-1](http://nlp.stanford.edu/~socherr/stanfordSentimentTreebank.zip) ) : ** این مجموعه دادگان نسخه گسترش یافته دیتاست MR است که بخش آموزش، تست و ارزیابی آن به طور جداگانه ارائه شده است. این دیتاست شامل 11855 جمله است که در پنج کلاس بسیار منفی، منفی، ممتنع، مثبت و بسیار مثبت دسته بندی شده اند.
+ **درخت مفهومی استنفرد 2 ([SST-2](http://nlp.stanford.edu/~socherr/stanfordSentimentTreebank.zip) ) : ** مشابه نسخه قبلی با این تفاوت که نظرات ممتنع حذف شده اند. به علاوه اینکه تعداد کلاس ها به دو کلاس مثبت و منفی کاهش یافته است.
+ **جملات فاعلی (Subj) : ** دسته بندی جملات موجود به دو گروه فاعلی و مفعولی.
+ **انواع پرسش ها ([TREC](http://cogcomp.org/Data/QA/QC/)) :** این دادگان شامل پرسش هایی است که در 6 گروه دسته بندی شده اند. کلاس های موجود عبارتند از: افراد، اماکن،  اعداد، توصیفات، ابزارها و موجودیت ها. تعداد کل پرسش های این مجموعه 5952 سوال است.
+ **نظرات مشتریان ([CR](https://www.cs.uic.edu/~liub/FBS/sentiment-analysis.html)):** این مجموعه دادگان شامل نظرات کاربران در مورد محصولاتی مثل دوربین، موبایل و... است که در دو دسته مثبت و منفی تقسیم بندی شده اند. 
+ **دیتاست [MPQA](http://mpqa.cs.pitt.edu/corpora/mpqa_corpus/) :** از این دادگان برای تشخیص مثبت یا منفی بودن نظرات کاربران استفاده می شود که شامل 10606 جمله است.

جزییات ابعاد این مجموعه دادگان را می توانید در جدول زیر مشاهده کنید. c تعداد کلاس ها، l متوسط طول جملات، |V| سایز دیکشنری، $|{ V }_{ pre }|$ تعداد واژه های بردار واژگان و Test سایز دادگان تست است. CV به این مفهوم است که از روش Cross Validation استفاده شده است.

| Test | $ { V }_{ pre } $ | V | N | l | c | Data | 
|:--:|:-----:|:-----:|:----:|:----:|:----:|:----:|
| CV  | 16448 | 18765 | 10662 | 20 | 2| MR |
| 2210 | 16262 | 17836 | 11855 | 18 | 5| SST-1  |
| 1821 | 14838 | 16185 | 9613 |19 | 2| SST-2|
| CV |  17913  | 21323 |10000| 23 |2| Subj|
| 500| 9125 | 9592 | 5952 | 10 | 6 | TREC|
|  CV | 5046| 5340| 3775| 19 | 2 | CR|
| CV | 6083| 6246| 10606 | 3 | 2 |MPQA|
> جدول 1: اطلاعات مربوط به مجموعه دادگان

+ **ًword2vec  **
علاوه بر مجموعه دادگان ذکر شده، بهتر است توضیحاتی نیز در مورد بردار واژگان از پیش آموزش دیده word2vec  مطرح کنیم. این پروژه توسط گوگل راه اندازی شده و با استفاده از پیکره های عظیم موجود اموزش دیده و  بازنمایش برداری  تعداد زیادی از کلمات را ارائه می دهد. بردارهای از پیش آموزش دیده این پژوهش در دسترس عموم است و می توان آنها را در بسیاری از کارهای NLP به کار برد. [14][15][16]
اگر با استفاده از روش های کاهش ابعاد، بردار چند بعدی کلمات را در صفحه دو بعدی نمایش دهیم خواهیم دید که بسیاری از معانی موجود در واژگان را می توان در روابط بین بردارها نیز مشاهده کرد. مثلا رابطه بین فعل هایی با ریشه یکسان اما زمان های متفاوت و یا رابطه بین کشورها و پایتخت آنها. به تصویر زیر توجه کنید. شرح بیشتر این مبحث از چهارچوب این گزارش خارج است توصیه می شود برای مطالعه بیشتر در این حوزه به منابع انتهای گزارش رجوع شود.
![شکل 5: ارتباط بین واژگان در فضای برداری](https://boute.s3.amazonaws.com/300-linear-relationships.jpg)


----------


### **7. آموزش شبکه**
برای تمامی دادگان به کار رفته در این پژوهش، مقادیر پارامترها به شرح زیر است:
+ اندازه فیلترها 3، 4 و 5 که هر یک از فیلترها با 100 وزن مختلف به کار رفته اند(مجموعا 300 نقشه ویژگی)
+ نرح dropout برابر 0.5
+ اندازه هر mini-batch برابر 50
+ برای دادگانی که مجموعه تست مجزا ندارند، 10 درصد از داده ها برای تست انتخاب شده است.(CV که پیش تر در بخش معرفی دادگان به آن اشاره شد)
+ در مرحله آموزش برای به روز کردن وزن ها از SGD [^Stochastic Gradient Descend]و قاعده Adaddelta استفاده شده است.
+ تابع فعال ساز RELU در تمام لایه ها (به جز لایه آخر)
+  نرخ آموزش[^Learning Rate] 0.03


----------


### **8. پیاده سازی**
پیاده سازی اصلی مقاله مورد بحث به زبان پایتون و با استفاده از ورژن 0.7 [تیانو](https://github.com/yoonkim/CNN_sentence)[^Theano] توسط شخص نویسنده انجام شده است. پس از انتشار مقاله، پیاده سازی های متفاوت دیگری با استفاده از ابزار های [تنسورفلو](https://github.com/dennybritz/cnn-text-classification-tf)[^Tensorflow] و [تورچ](https://github.com/harvardnlp/sent-conv-torch)[^Torch] انجام شده که سورس کد آنها نیز در دسترس است. 
نکته قابل ذکر دیگر اینکه در زمان انجام این پژوهش به دلیل عدم دسترسی محقق به GPU برخی مسائل از جمله تاثیر استفاده از تعبیه متن های دیگر مثل GloVe ، تاثیر اندازه و تعداد فیلترهای مختلف و k-max pooling بر عملکرد مدل بررسی نشده است. در سال 2015 در مقاله دیگری توسط [ژانگ](http://arxiv.org/abs/1510.03820)  به بررسی دقیق تر این موارد پرداخته شده است . در دو جدول زیر نمونه هایی از این بررسی های تکمیلی ارائه شده است. برای جزییات دقیق تر به مرجع [13] مراجعه کنید.


| non-static word2vec+GloVe-CNN | non-static GloVe-CNN | non-static word2vec-CNN | Dataset| 
|:----:|:----:|:----:|:----:|
| 81.02| 81.03| 81.24| MR |
| 45.98| 45.65| 47.08| SST-1  |
| 85.45|85.22| 85.49| SST-2|
|93.66| 93.64|93.20| Subj|
| 91.37| 90.38| 91.54 | TREC|
| 84.65| 84.33| 83.92 | CR|
| 89.55| 89.57 | 89.32 |MPQA|
> جدول 2: تاثیر استفاده از بردارهای تعبیه متن مختلف word2vec و GloVe


| Accuracy | Multiple Region Size | 
|:----:|:----:|
 | 81.65| (7) |
| 81.24| (3,4,5)|
| 81.28| (4,5,6)|
|81.57| (5,6,7)|
| 81.69 | (7,8,9)|
| 81.52 |(10,11,12)|
| 81.53| (11,12,13)|
|81.43| (3,4,5,6)|
| 81.62 | (6,7,8,9)|
| 81.63| (7,7,7)|
|**81.73**| (7,7,7,7)|
> جدول 3: تاثیر استفاده از فیلترهایی با اندازه و تعداد مختلف بر روی دقت مدل بر مجموعه دادگان MR


----------


### **9. نتایج**
جدول زیر دقت حاصل از مدل فعلی را نشان می دهد و مقایسه بین این نتایج با مدل های پیشین نشان از برتری روش ما دارد .  از این جدول می توان نتیجه گرفت که استفاده از بردارهای از پیش آموزش دیده می تواند منجر به بهبود نتایج شود.

| Model            | MR | SST-1 | SST-2 | Subj | TREC | CR   | MPQA |
|:----------------:|:--:|:-----:|:-----:|:----:|:----:|:----:|:----:|
| **CNN-rand**         | 76.1 | 45.0 | 82.7 | 89.6 | 91.2 | 79.8 | 83.4 |
| **CNN-static**       | 81.0 | 45.5 | 86.8 | 93.0 | 92.8 | 84.7 | **89.6**  |
| **CNN-non-static**   | **81.5** | 48.0 | 87.2 | 93.4 |93.6 | 84.3 | 89.5   |
| **CNN-multichannel** |  81.1  | 47.4 |**88.1**| 93.2 |92.2 |**85.0** | 89.4   |
| **RAE**| 77.7 | 43.2 | 82.4 | - | - | - | 86.4   |
| **MV-RNN** | 79.0| 44.4| 82.9| -| - | -| -|
| **RNTN** | -| 45.7| 85.4| -| - | -| -  |
| **DCNN**   | - | 48.5| 86.8| -|93.0| -| -|
| **Paragraph-Vec** |  -| **48.7**|87.8| -|-|- | -|
| **CCAE** | 77.8| -| -| -| -| -| 87.2|
| **Sent-Parser**| 79.5| -| -| - | -| -| 86.3  |
| **NBSVM**   | 79.4 | - | - | 93.2 |-| 81.8 | 86.3|
| **MNB** |  79.0| -|-| **93.6** |-|80.0 | 86.3|
| **G-Dropout**  | 79.0| -| -| 93.4| -| 82.1| 86.1|
| **F-Dropout**  | 79.1| - | -| **93.6**| -| 81.9| 86.3  |
| **Tree-CRF**   | 77.3 | - | -| - |-| 81.4| 86.1   |
| **CRF-PR** |  -| -|-| -|-|82.7 | -   |
| **SVMs** |  -| - |-| - |**95.0**|- | -   |
> جدول 4: اطلاعات مربوط به دقت مدل های پیشنهادی و مقایسه آنها با مدل های پیشین

همان گونه که در جدول فوق مشاهده می شود مدل CNN-rand به تنهایی عملکرد چندان خوبی ندارد در حالی که مدل هایی که از بردارهای از پیش اموزش دیده استفاده کرده اند عملکرد بهتری دارند. در ادامه نتایج حاصله در دو گروه بررسی می شوند. اول مقایسه مدل های تک کاناله و چند کاناله[^multichannel] و سپس مقایسه  مدل های ایستا[^static] و غیرایستا. 

+ **تک کاناله در مقابل چند کاناله**
فرض اولیه ما بر این بود که مدل چندکاناله با جلوگیری از بیش برازش بهتر از مدل یک کاناله عمل می کند. به خصوص در مجموعه  دادگانی با حجم کم. اما طبق نتایج به دست آمده این برتری خیلی قاطعانه نیست.

+ **ایستا در مقابل غیر ایستا**
در مدل های غیر ایستا  بردارهای واژگان در جریان آموزش آپدیت می شوند. به عنوان مثال شبیه ترین کلمات به بردار کلمه good در حالت ایستا کلمات great, bad, terrific و  decent هستند در حالی که بعد از تکمیل آموزش شبیه ترین کلمات به آن nice, decent, solid و  terrific هستند. از نظر مفهومی نیز کلمه good بیشتر به کلمه nice شباهت دارد تا great و این موضوع در نتایج حاصله مشهود است. همچنین در مورد کلمه bad درحالت ایستا good, terrible, horrible و  lousy و در حالت غیرایستا کلمات terrible, horrible, lousy و stupid به عنوان شبیه ترین بردار به دست می آیند.


----------

### **10.## ** بخش دوم: پیاده سازی**
در این بخش سعی داریم جزییاتی از پیاده سازی مقاله که به زبان پایتون و با استفاده از تنسورفلو انجام شده است را مختصرا شرح دهیم. سورس کد در این [لینک](https://github.com/dennybritz/cnn-text-classification-tf) در دسترس است. نحوه اجرای کد و لود کردن دادگان در لینک ذکر شده است و ما از بیان مجدد آن خودداری می کنیم.

### ** پیش پردازش داده**
این بخش شامل 4 فعالیت اصلی است:
+ لود کردن داده های مثبت و منفی از متن خام دادگان.
+ تفکیک جملات، تبدیل حروف بزرگ به حروف کوچک، تفکیک وازگان، جداسازی علائم نگارشی و حذف فواصل بیش از دوتا که به مجموعه این اعمال اصطلاحا تمیز کردن[^data cleaning] داده می گویند.
+ تشخیص بلندترین جمله و افزودن توکن های pad  به انتهای جملات کوتاه تر به منظور نرمال شدن طول تمام جملات.
+ ساخت مجموعه دیکشنری و تخصیص دادن یک شناسه متناسب برای هر کلمه. بدین ترتیب هر کلمه به صورت بردار one-hot در می اید.

### ** پیاده سازی مدل**
همان گونه که در کد زیر مشاهده می کنید تعریف مدل ما در کلاس TextCNN انجام شده که پارامترهای مورد نیاز ورودی را دریافت می کند:
+ ً sequence_length :طول جملات که 59 است. همان گونه که پیش تر بیان شد، همه جملات را در مرحله پیش پردازش با افزودن توکن هایی، هم اندازه ی بلندترین جمله کردیم.
+ ًnum_classes :تعداد کلاس ها. که دو کلاس مثبت و منفی است.
+ ًvocab_size :اندازه دیکشنری. که در این دادگان 18765 است.
+ ًembedding_size : ابعاد تعبیه متن که 128 در نظر گرفته شده است.
+ ًfilter_sizes :ما از سه اندازه [3, 4, 5] استفاده کردیم.
+ ًnum_filters :تعداد فیلترها از هر اندازه 128 تا است.

برای تعریف متغیرها در تنسورفلو از دو مفهوم استفاده می شود: variable و placeholder . مورد اول برای تعریف پارامتر و مقدار دهی به آن استفاده می شود. placeholder ها زمانی استفاده می شوند که می خواهیم پارامترهایی را تعریف کنیم و در زمان اجرا[^execute] مقادیر انها را مشخص کنیم. placeholder معمولا برای ورودی و خروجی و برچسب ها استفاده می شود. variable برای ماتریس وزن ها و بایاس و سایر پارامترها. در این بخش کد برای جملات ورودی و برچسب داده ها از placeholder استفاده شده است. همچنین برای مقدار احتمال تابع dropout که به صورت پیش فرض 0.5 در نظر گرفته شده است.


	class TextCNN(object):
	   def __init__(
	      self, sequence_length, num_classes, vocab_size,
	      embedding_size, filter_sizes, num_filters, l2_reg_lambda=0.0):
	      self.input_x = tf.placeholder(tf.int32, [None, sequence_length], name="input_x")
         self.input_y = tf.placeholder(tf.float32, [None, num_classes], name="input_y")
         self.dropout_keep_prob = tf.placeholder(tf.float32, name="dropout_keep_prob")
+ ً(device("/cpu:0" : به طور پیش فرض تنسورفلو سعی می کند در صورت وجود GPU عمل ها را بر روی آن اجرا کند اما این دستور به ما اجازه می دهد که کد را بر روی cpu اجرا کنیم.
+ ًtf.name_scope : این تابع به ما این امکان را می دهد که تمام اعمال مرتبط با لایه embedding را در سوپرنودی [^top-level node] قرار دهیم به نام embedding . نتیجه این کار به ما ساختاری سلسله مراتبی [^hierarchy] می دهد که در زمان مصور کردن [^visualize] مدل  و نتایج بسیار مفید است. مصورسازی در محیطی به نام تنسوربورد[^TensorBoard] انجام می شود.
+ ًW : این متغیر ماتریس تعبیه متن مدل ماست که در طول فرایند آموزش یاد گرفته می شود.
نتیجه لایه تعبیه متن تنسوری با ابعاد [None, sequence_length, embedding_size, 1] است.


	with tf.device('/cpu:0'), tf.name_scope("embedding"):
            self.W = tf.Variable(
                tf.random_uniform([vocab_size, embedding_size], -1.0, 1.0),
                name="W")
            self.embedded_chars = tf.nn.embedding_lookup(self.W, self.input_x)
            self.embedded_chars_expanded = tf.expand_dims(self.embedded_chars, -1)
            

به منظور ساخت لایه پیچشی از conv2d استفاده می کنیم.در اینجا w ماتریس فیلتر ما و h نتیجه اعمال یک تابع غیرخطی بر خروجی لایه پیچشی است. خروجی این مرحله تنسوری با ابعاد [1, sequence_length - filter_size + 1, 1, 1] است. اعمال لایه max-pooling بر روی این خروجی تنسوری با ابعاد [batch_size, 1, 1, num_filters] نتیجه می دهد. چنین ماتریسی برای هر فیلتری به دست می آید و با ترکیب آنها به یک بردار با ابعاد [batch_size, num_filters_total] می رسیم.


	pooled_outputs = []
        for i, filter_size in enumerate(filter_sizes):
            with tf.name_scope("conv-maxpool-%s" % filter_size):
                # Convolution Layer
                filter_shape = [filter_size, embedding_size, 1, num_filters]
                W = tf.Variable(tf.truncated_normal(filter_shape, stddev=0.1), name="W")
                b = tf.Variable(tf.constant(0.1, shape=[num_filters]), name="b")
                conv = tf.nn.conv2d(
                    self.embedded_chars_expanded,
                    W,
                    strides=[1, 1, 1, 1],
                    padding="VALID",
                    name="conv")
                # Apply nonlinearity
                h = tf.nn.relu(tf.nn.bias_add(conv, b), name="relu")
                # Maxpooling over the outputs
				# h --> 4d
                pooled = tf.nn.max_pool(
                    h,
                    ksize=[1, sequence_length - filter_size + 1, 1, 1],
                    strides=[1, 1, 1, 1],
                    padding='VALID',
                    name="pool")
                pooled_outputs.append(pooled)
        # Combine all the pooled features
        num_filters_total = num_filters * len(filter_sizes)
        self.h_pool = tf.concat(pooled_outputs, 3)
        self.h_pool_flat = tf.reshape(self.h_pool, [-1, num_filters_total])
  
  برای اعمال لایه dropout تنها لازم است احتمال فعال بودن هر نورون را به عنوان ورودی به تابع tf.nn.dropout بدهیم که به طور پیش فرض 0.5 در نظر گرفته شده است. توجه کنید که این مقدار در هنگام تست 1 قرار می گیرد. به عبارتی تمام نورون ها در مرحله تست فعال اند و نورون غیرفعالی نداریم.

	with tf.name_scope("dropout"):
            self.h_drop = tf.nn.dropout(self.h_pool_flat, self.dropout_keep_prob)

با ضرب کردن ماتریس ورودی در وزن ها و جمع آن با بایاس می توانیم برای هر داده مقداری به نام امتیاز[^score]  محاسبه کنیم و برچسب کلاسی پیش بینی کنیم. (بزرگترین مقدار امتیاز می شود برچسب کلاس) همچنین می توان با استفاده از تابع softmax مقادیر امتیاز را به احتمال تبدیل کرد.

	with tf.name_scope("output"):
            W = tf.get_variable(
                "W",
                shape=[num_filters_total, num_classes],
                initializer=tf.contrib.layers.xavier_initializer())
            b = tf.Variable(tf.constant(0.1, shape=[num_classes]), name="b")
            self.scores = tf.nn.xw_plus_b(self.h_drop, W, b, name="scores")
            self.predictions = tf.argmax(self.scores, 1, name="predictions")

با استفاده از مقادیر امتیاز محاسبه شده در بخش قبل مقدار زیان [^loss] را حساب می کنیم. زیان در حقیقت همان خطای شبکه ما است که سعی داریم مقدار آن را کمینه کنیم. در پیاده سازی حاضر از تابع زیان cross-entropy استفاده شده است. که برای هر کلاس مقدار زیان را محاسبه می کند و از نتایج میانگین میگیرد.
همچنین تعریف و محاسبه پارامتری به نام دقت[^accuracy] و دنبال کردن مقادیر آن در طول پروسه آموزش می تواند مفید باشد.

	with tf.name_scope("loss"):
            losses = tf.nn.softmax_cross_entropy_with_logits(logits=self.scores, labels=self.input_y)
            self.loss = tf.reduce_mean(losses) + l2_reg_lambda * l2_loss
    with tf.name_scope("accuracy"):
            correct_predictions = tf.equal(self.predictions, tf.argmax(self.input_y, 1))
            self.accuracy = tf.reduce_mean(tf.cast(correct_predictions, "float"), name="accuracy")


### ** روند  آموزش** 
تنسورفلو برای اجرای مدل از session استفاده می کند. هر session یک گراف را اجرا می کند. هر گراف شامل عمل ها [^operations] و تنسورهایی است. عمل ها همان نودهای گراف و تنسورها یال های آن هستند.
+ ًallow_soft_placement :اگر این گزینه فعال نباشد اجرای کد بر روی ماشینی بدون gpu می تواند منتج به خطا شود.
+ ًlog_device_placement : با فعال بودن این گزینه می توانیم تعیین کنیم اعمال ما بر روی چه دیوایسی اجرا شوند(cpu یا gpu).


	with tf.Graph().as_default():
    session_conf = tf.ConfigProto(
      allow_soft_placement=FLAGS.allow_soft_placement,
      log_device_placement=FLAGS.log_device_placement)
    sess = tf.Session(config=session_conf)
 
 سپس مقادیر شبکه مورد نظر ما تعیین می شوند و گراف ساخته می شود:

	cnn = TextCNN(
            sequence_length=x_train.shape[1],
            num_classes=y_train.shape[1],
            vocab_size=len(vocab_processor.vocabulary_),
            embedding_size=FLAGS.embedding_dim,
            filter_sizes=list(map(int, FLAGS.filter_sizes.split(","))),
            num_filters=FLAGS.num_filters,
            l2_reg_lambda=FLAGS.l2_reg_lambda)

در مرحله بعد لازم است مشخص کنیم که برای بهینه سازی تابع زیان از چه روشی استفاده شود. ما از adam استفاده کرده ایم که در عمل هم بسیار خوب کار می کند. در اینجا train_op  برای اعمال به روز رسانی وزن ها بر حسب مقادیر گرادیان های محاسبه شده تعریف شده است. در حقیقت هر بار اجرای train_op  حکم یک گام در آموزش[^training step] را دارد. 
متغیر global_step این امکان را فراهم می سازد تا تنسورفلو به صورت اتوماتیک گام های آموزش را بشمارد. global_step با هر بار اجرای train_op  یک واحد افزایش می یابد.

	global_step = tf.Variable(0, name="global_step", trainable=False)
        optimizer = tf.train.AdamOptimizer(1e-3)
        grads_and_vars = optimizer.compute_gradients(cnn.loss)
        train_op = optimizer.apply_gradients(grads_and_vars, global_step=global_step)

یکی از مفاهیم کاربردی در تنسورفلو خلاصه برداری [^summary] است که به ما این امکان را می دهد تا مقادیر پارامترهای مهم را ذخیره و دنبال کنیم و آنها را مصور کنیم. نحوه استفاده از این امکان  در قطعه کد زیر نمایش داده شده است.

	# Summaries for loss and accuracy
        loss_summary = tf.summary.scalar("loss", cnn.loss)
        acc_summary = tf.summary.scalar("accuracy", cnn.accuracy)
        # Train Summaries
        train_summary_op = tf.summary.merge([loss_summary, acc_summary, grad_summaries_merged])
        train_summary_dir = os.path.join(out_dir, "summaries", "train")
        train_summary_writer = tf.summary.FileWriter(train_summary_dir, sess.graph)

مفهوم کاربردی دیگر مبحث نشان گذاری  [^checkpointing] است که در آن پارامترهای مدل ذخیره می شوند تا در آینده آموزش را از آن نقطه ادامه دهیم. نحوه استفاده از این امکان  در قطعه کد زیر نمایش داده شده است.

	# Checkpoint directory. Tensorflow assumes this directory already exists so we need to create it
        checkpoint_dir = os.path.abspath(os.path.join(out_dir, "checkpoints"))
        checkpoint_prefix = os.path.join(checkpoint_dir, "model")
        if not os.path.exists(checkpoint_dir):
            os.makedirs(checkpoint_dir)
        saver = tf.train.Saver(tf.global_variables(), max_to_keep=FLAGS.num_checkpoints)

پیش از این که بتوانیم مدل را اجرا کنیم نیاز داریم تا تمام مقداردهنده ها  [^initializer]  را فعال کنیم و مقادیر اولیه پارامترها تخصیص داده شوند. این عمل با دستور زیر انجام می شود:

	sess.run(tf.global_variables_initializer())

حال نیاز داریم هر یک گام آموزش را تعریف کنیم. هر گام شامل لود کردن یک بچ از دادگان، اجرای مدل بر روی دیتا، محاسبات تابع زیان و به روز رسانی مقادیر پارامترهاست.

	def train_step(x_batch, y_batch):
            """
            A single training step
            """
            feed_dict = {
              cnn.input_x: x_batch,
              cnn.input_y: y_batch,
              cnn.dropout_keep_prob: FLAGS.dropout_keep_prob
            }
            _, step, summaries, loss, accuracy = sess.run(
                [train_op, global_step, train_summary_op, cnn.loss, cnn.accuracy],
                feed_dict)
            time_str = datetime.datetime.now().isoformat()
            print("{}: step {}, loss {:g}, acc {:g}".format(time_str, step, loss, accuracy))
            train_summary_writer.add_summary(summaries, step)

بعد از تعریف هر گام آموزش باید حلقه آموزش را تعریف کنیم. این حلقه شامل گردش بر روی بچ های مختلف داده و اجرای یک گام در هر بار است. در گام های خاصی از حلقه هم نیاز به خلاصه برداری یا نشان گذاری وجود دارد که در حلقه به انها اشاره شده است.

	for batch in batches:
            x_batch, y_batch = zip(*batch)
            train_step(x_batch, y_batch)
            current_step = tf.train.global_step(sess, global_step)
            if current_step % FLAGS.evaluate_every == 0:
                print("\nEvaluation:")
                dev_step(x_dev, y_dev, writer=dev_summary_writer)
                print("")
            if current_step % FLAGS.checkpoint_every == 0:
                path = saver.save(sess, checkpoint_prefix, global_step=current_step)
                print("Saved model checkpoint to {}\n".format(path))

### ** مصورسازی** 
پس از آموزش مدل با پارامترهای پیش فرض بر روی داده های MR نمودار مقادیر تابع زیان و همچنین دقت به شکل زیر است. خط آبی مربوط به آموزش و خط قرمز مربوط به تست است. با توجه به اینکه مقدار دقت تست بسیار کمتر از دقت آموزش است می توان نتیجه گرفت که بیش برازش[^overfit] رخ داده است.
![نمودار تغییرات مقدار تابع زیان](https://boute.s3.amazonaws.com/300-loss.png)

![نمودار تغییرات دقت](https://boute.s3.amazonaws.com/300-acc.png)
        
----------

## ** مراجع**
[1] Joachims, Thorsten. _Learning to classify text using support vector machines: Methods, theory and algorithms_. Kluwer Academic Publishers, 2002.
[2] Chen, Jingnian, Houkuan Huang, Shengfeng Tian, and Youli Qu. "Feature selection for text classification with Naïve Bayes." _Expert Systems with Applications_ 36, no. 3 (2009): 5432-5435.
[3] Mikolov, Tomas, Ilya Sutskever, Kai Chen, Greg S. Corrado, and Jeff Dean. "Distributed representations of words and phrases and their compositionality." In _Advances in neural information processing systems_, pp. 3111-3119. 2013.
[4]Pennington, Jeffrey, Richard Socher, and Christopher Manning. "Glove: Global vectors for word representation." In _Proceedings of the 2014 conference on empirical methods in natural language processing (EMNLP)_, pp. 1532-1543. 2014.
[5]W. Yih, X. He, C. Meek. 2014. Semantic Parsing for Single-Relation Question Answering. _In Proceedings
of ACL 2014_.
[6]Y. Shen, X. He, J. Gao, L. Deng, G. Mesnil. 2014. Learning Semantic Representations Using Convolutional Neural Networks forWeb Search._ In Proceedings of WWW 2014_.
[7]N. Kalchbrenner, E. Grefenstette, P. Blunsom. 2014. A Convolutional Neural Network for Modelling Sentences. _In Proceedings of ACL 2014_.
[8][Online]. Available: http://www.wildml.com/2015/11/understanding-convolutional-neural-networks-for-nlp/. [Accessed 20 12 2017].
[9][Online]. Available: https://adeshpande3.github.io/A-Beginner%27s-Guide-To-Understanding-Convolutional-Neural-Networks-Part-2/. [Accessed 20 12 2017].
[10]Srivastava, Nitish, Geoffrey E. Hinton, Alex Krizhevsky, Ilya Sutskever, and Ruslan Salakhutdinov. "Dropout: a simple way to prevent neural networks from overfitting." _Journal of machine learning research_ 15, no. 1 (2014): 1929-1958.
"Github,"[Online]. Available: http://cs231n.github.io/linear-classify/#softmax.[Accessed 20 12 2017].[11]
[12][Online]. Available: https://en.wikipedia.org/wiki/Softmax_function.. [Accessed 20 12 2017].
[13]Zhang, Ye, and Byron Wallace. "A sensitivity analysis of (and practitioners' guide to) convolutional neural networks for sentence classification." _arXiv preprint arXiv:1510.03820_(2015).
[14] "Github," [Online]. Available: https://github.com/dav/word2vec. [Accessed 20 12 2017].
[15][Online]. Available: https://blog.acolyer.org/2016/04/21/the-amazing-power-of-word-vectors/. [Accessed 20 12 2017].
[16][Online]. Available: https://code.google.com/archive/p/word2vec/. [Accessed 20 12 2017].