In this tutorial we will go through how to do Amharic text classification on our new dataset. This is the Github repo if you want to follow along.
%matplotlib inline
import matplotlib
import pandas as pd
import numpy as np
from sklearn.utils import shuffle
np.random.seed(42)
Exploratory Data Analysis
data = pd.read_csv('data/Amharic News Dataset.csv')
data = shuffle(data)
data.head()
headline | category | date | views | article | link | |
---|---|---|---|---|---|---|
15168 | แ แแแต แจแแดแซแแตแต แแญแแฝ แแญ แฅแจแฐแซแ แแ แ แแแต แ แฅแจแฐแฐแกแ แฅแซแ... | แแแญ แ แแ แแ | 26-Sep-20 | 2,204 | แ แฒแต แ แ แฃแฃ แแตแจแจแ 16แฃ 2013 (แคแ.แข.แฒ) แ แแแต แจแแดแซแแตแต ... | https://www.fanabc.com/%e1%88%85%e1%8b%88%e1%8... |
13721 | แ แถ แฐแแ แแฎแแ แจแฐแแต แจแ แแชแซ แแแต แแฉ แแแฅแญแฐแ แแญ แฐแแซแฉ | แแแญ แ แแ แแ | 1-Dec-20 | 336 | แ แฒแต แ แ แฃแฃ แ แณแญ 22แฃ 2013 (แคแ.แข.แฒ) แจแขแแดแช แแญแตแ แ แ แแญ... | https://www.fanabc.com/%e1%8a%a0%e1%89%b6-%e1%... |
21287 | แ แ แฒแต แแแต แแแ แ แซแต แฐแแฝ แ แแญแณแแ แฐแฝแจแญแซแชแแฝ แฐแแญแฐแ แแฑ | แแแฒแซ | 15-Sep-19 | Unknown | แจ2011 แ.แ. แซแ แแแ แจแแแแ แตแญแแต แ แฐแแ แแ แแแแ แแแต แข... | https://www.ethiopianreporter.com/article/16750 |
16264 | โแจแ แณแด แแตแก แ แแ แฑ แ แแ แฉ แจแฆแญแต แฐแฅแณแข แ แตแซแ แแฃแชแแต แแจแแฐแ... | แแแญ แ แแ แแ | 18-Jun-20 | 1,751 | แ แฒแต แ แ แฃแฃ แฐแ 11แฃ 2012 (แคแ. แข.แฒ) แจแ แณแด แแตแฅ แแแฃแณ แ ... | https://www.fanabc.com/%e1%8b%a8%e1%88%85%e1%8... |
24513 | แ แฆแฎแแซ แญแแ แแฅแจแถแปแธแ แจแแฐแแฃแธแ แขแแจแตแฐแฎแฝ แจแซแณ แฅแซแ แฅแซแแจ... | แแแฒแซ | 23-Oct-16 | Unknown | แ แแตแจแจแ แแญ แแจแจแป แ แฆแฎแแซ แญแแ แ แฐแแณแ แแจแต แแฅแจแถแปแธแ แจแแฐ... | https://www.ethiopianreporter.com/content/%E1%... |
data.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 50708 entries, 15168 to 15795
Data columns (total 6 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 headline 50694 non-null object
1 category 50706 non-null object
2 date 50707 non-null object
3 views 50707 non-null object
4 article 50707 non-null object
5 link 50706 non-null object
dtypes: object(6)
memory usage: 2.7+ MB
data = data.dropna(subset=['article'])
data['link'].value_counts()
https://amharic.voanews.com//a/ethiopia-tigrai-mekele/5679989.html 3
https://amharic.voanews.com//a/rev-jesse-jackson-letter-to-hon-karen-bass-about-nile-river-5-21-2020o/5430577.html 3
https://amharic.voanews.com//a/white-house-on-river-nile-and-ethiopias-dam-10-03-19/5110165.html 3
https://amharic.voanews.com//a/sudan-laws/5499793.html 3
https://amharic.voanews.com//a/covid-ethiopia/5470709.html 3
..
https://am.al-ain.com/article/an-interim-experts-summit-is-to-be-formed-to-resolve-differences-among-olf-members 1
https://soccerethiopia.net/football/7715 1
https://waltainfo.com/am/29558/ 1
https://www.addisadmassnews.com/index.php?option=com_k2&view=item&id=20922:%E1%89%A02050-%E1%8A%A8%E1%8A%A0%E1%88%88%E1%88%9B%E1%89%BD%E1%8A%95-%E1%88%85%E1%8B%9D%E1%89%A5-%E1%88%A9%E1%89%A5-%E1%8B%AB%E1%88%85%E1%88%89-%E1%8A%A0%E1%8D%8D%E1%88%AA%E1%8A%AB%E1%8B%8D%E1%8B%AB%E1%8A%95-%E1%8B%AD%E1%88%86%E1%8A%93%E1%88%89-%E1%89%B0%E1%89%A3%E1%88%88&Itemid=212 1
https://waltainfo.com/am/28427/ 1
Name: link, Length: 50008, dtype: int64
data.category.unique()
array(['แแแญ แ แแ แแ', 'แแแฒแซ', 'แตแแญแต', 'แแแ แ แแ แแ', 'แขแแแต', 'แแแแ', nan],
dtype=object)
data['word_len'] = data['article'].str.split().str.len()
data.head()
headline | category | date | views | article | link | word_len | |
---|---|---|---|---|---|---|---|
15168 | แ แแแต แจแแดแซแแตแต แแญแแฝ แแญ แฅแจแฐแซแ แแ แ แแแต แ แฅแจแฐแฐแกแ แฅแซแ... | แแแญ แ แแ แแ | 26-Sep-20 | 2,204 | แ แฒแต แ แ แฃแฃ แแตแจแจแ 16แฃ 2013 (แคแ.แข.แฒ) แ แแแต แจแแดแซแแตแต ... | https://www.fanabc.com/%e1%88%85%e1%8b%88%e1%8... | 206 |
13721 | แ แถ แฐแแ แแฎแแ แจแฐแแต แจแ แแชแซ แแแต แแฉ แแแฅแญแฐแ แแญ แฐแแซแฉ | แแแญ แ แแ แแ | 1-Dec-20 | 336 | แ แฒแต แ แ แฃแฃ แ แณแญ 22แฃ 2013 (แคแ.แข.แฒ) แจแขแแดแช แแญแตแ แ แ แแญ... | https://www.fanabc.com/%e1%8a%a0%e1%89%b6-%e1%... | 141 |
21287 | แ แ แฒแต แแแต แแแ แ แซแต แฐแแฝ แ แแญแณแแ แฐแฝแจแญแซแชแแฝ แฐแแญแฐแ แแฑ | แแแฒแซ | 15-Sep-19 | Unknown | แจ2011 แ.แ. แซแ แแแ แจแแแแ แตแญแแต แ แฐแแ แแ แแแแ แแแต แข... | https://www.ethiopianreporter.com/article/16750 | 206 |
16264 | โแจแ แณแด แแตแก แ แแ แฑ แ แแ แฉ แจแฆแญแต แฐแฅแณแข แ แตแซแ แแฃแชแแต แแจแแฐแ... | แแแญ แ แแ แแ | 18-Jun-20 | 1,751 | แ แฒแต แ แ แฃแฃ แฐแ 11แฃ 2012 (แคแ. แข.แฒ) แจแ แณแด แแตแฅ แแแฃแณ แ ... | https://www.fanabc.com/%e1%8b%a8%e1%88%85%e1%8... | 261 |
24513 | แ แฆแฎแแซ แญแแ แแฅแจแถแปแธแ แจแแฐแแฃแธแ แขแแจแตแฐแฎแฝ แจแซแณ แฅแซแ แฅแซแแจ... | แแแฒแซ | 23-Oct-16 | Unknown | แ แแตแจแจแ แแญ แแจแจแป แ แฆแฎแแซ แญแแ แ แฐแแณแ แแจแต แแฅแจแถแปแธแ แจแแฐ... | https://www.ethiopianreporter.com/content/%E1%... | 461 |
data.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 50707 entries, 15168 to 15795
Data columns (total 7 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 headline 50694 non-null object
1 category 50706 non-null object
2 date 50707 non-null object
3 views 50707 non-null object
4 article 50707 non-null object
5 link 50706 non-null object
6 word_len 50707 non-null int64
dtypes: int64(1), object(6)
memory usage: 3.1+ MB
data.word_len.mean()
249.65586605399648
character level normalization
Amharic has characters wich have the same sound that can be interchangably used.
for example letters โแโ,โแ โ,โแโ,โแโ,โแโ,โแปโ,โแโ have the same sound so we change them to โแโ
import re
#method to normalize character level missmatch such as แธแแญ and แแแญ
def normalize_char_level_missmatch(input_token):
rep1=re.sub('[แแ
แแแแป]','แ',input_token)
rep2=re.sub('[แแแ
]','แ',rep1)
rep3=re.sub('[แแแบ]','แ',rep2)
rep4=re.sub('[แแแ]','แ',rep3)
rep5=re.sub('[แแ
]','แ
',rep4)
rep6=re.sub('[แแแพ]','แ',rep5)
rep7=re.sub('[แ ]','แฐ',rep6)
rep8=re.sub('[แก]','แฑ',rep7)
rep9=re.sub('[แข]','แฒ',rep8)
rep10=re.sub('[แฃ]','แณ',rep9)
rep11=re.sub('[แค]','แด',rep10)
rep12=re.sub('[แฅ]','แต',rep11)
rep13=re.sub('[แฆ]','แถ',rep12)
rep14=re.sub('[แแฃแ]','แ ',rep13)
rep15=re.sub('[แ]','แก',rep14)
rep16=re.sub('[แ]','แข',rep15)
rep17=re.sub('[แ]','แค',rep16)
rep18=re.sub('[แ]','แฅ',rep17)
rep19=re.sub('[แ]','แฆ',rep18)
rep20=re.sub('[แธ]','แ',rep19)
rep21=re.sub('[แน]','แ',rep20)
rep22=re.sub('[แบ]','แ',rep21)
rep23=re.sub('[แป]','แ',rep22)
rep24=re.sub('[แผ]','แ',rep23)
rep25=re.sub('[แฝ]','แ
',rep24)
rep26=re.sub('[แพ]','แ',rep25)
#Normalizing words with Labialized Amharic characters such as แ แแฑแแ or แ แแฑแ แ to แ แแทแ
rep27=re.sub('(แ[แแ ])','แ',rep26)
rep28=re.sub('(แ[แแ ])','แ',rep27)
rep29=re.sub('(แฑ[แแ ])','แท',rep28)
rep30=re.sub('(แฉ[แแ ])','แฏ',rep29)
rep31=re.sub('(แฑ[แแ ])','แท',rep30)
rep32=re.sub('(แน[แแ ])','แฟ',rep31)
rep33=re.sub('(แ[แแ ])','แ',rep32)
rep34=re.sub('(แก[แแ ])','แง',rep33)
rep35=re.sub('(แน[แแ ])','แฟ',rep34)
rep36=re.sub('(แ[แแ ])','แ',rep35)
rep37=re.sub('(แ[แแ ])','แ',rep36)
rep38=re.sub('(แ[แแ ])','แ',rep37)
rep39=re.sub('(แฉ[แแ ])','แณ',rep38)
rep40=re.sub('(แ[แแ ])','แ',rep39)
rep41=re.sub('(แ[แแ ])','แ',rep40)
rep42=re.sub('(แฐ[แแ ])','แท',rep41)
rep43=re.sub('(แก[แแ ])','แง',rep42)
rep44=re.sub('(แฉ[แแ ])','แฏ',rep43)
rep45=re.sub('(แน[แแ ])','แฟ',rep44)
rep46=re.sub('(แ[แแ ])','แ',rep45)
rep47=re.sub('[แ]','แ',rep46) #แ can be written as แ
rep48=re.sub('[แต]','แฉ',rep47) #แฉ can be also written as แต
return rep48
data['article'] = data['article'].str.replace('[^\w\s]','')
data['article'] = data['article'].apply(lambda x: normalize_char_level_missmatch(x))
n_data = data[['article','category']]
n_data.head()
text,label = data['article'].values,data['category'].values
# n_data.head(5).to_csv('table.csv')
Naive Bays - CountVectorizer
from sklearn.feature_extraction.text import CountVectorizer
matrix = CountVectorizer(analyzer='word',max_features=1000,ngram_range=(1, 3))
X = matrix.fit_transform(text).toarray()
X
array([[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[1, 0, 0, ..., 0, 0, 5],
...,
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0],
[0, 0, 0, ..., 0, 0, 0]])
unique_label = list(set(label))
Y= []
for i in label:
Y.append(unique_label.index(i))
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, Y,test_size=0.2)
# Naive Bayes
from sklearn.naive_bayes import GaussianNB
classifier = GaussianNB()
classifier.fit(X_train, y_train)
# Predict Class
y_pred = classifier.predict(X_test)
# Accuracy
from sklearn.metrics import accuracy_score
accuracy = accuracy_score(y_test, y_pred)
accuracy
0.6220666535200158
from sklearn.metrics import classification_report
print(classification_report(y_test, y_pred, target_names=['แตแแญแต', 'แแแแ', 'แแแญ แ แแ แแ', 'แขแแแต', 'แแแ แ แแ แแ', 'แแแฒแซ', 'nan']))
precision recall f1-score support
แตแแญแต 0.82 0.39 0.53 4154
แแแแ 0.37 0.72 0.49 762
แแแญ แ แแ แแ 0.00 0.00 0.00 0
แขแแแต 0.44 0.90 0.59 1334
แแแ แ แแ แแ 0.96 0.94 0.95 1934
แแแฒแซ 0.59 0.55 0.57 1808
nan 0.35 0.78 0.48 150
accuracy 0.62 10142
macro avg 0.50 0.61 0.52 10142
weighted avg 0.72 0.62 0.62 10142
Naive Bays - tf -df
from sklearn.feature_extraction.text import TfidfVectorizer
matrix = TfidfVectorizer(analyzer='word',max_features=1000,ngram_range=(1, 3))
X = matrix.fit_transform(text).toarray()
X
array([[0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[0.06538107, 0. , 0. , ..., 0. , 0. ,
0.33719558],
...,
[0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[0. , 0. , 0. , ..., 0. , 0. ,
0. ],
[0. , 0. , 0. , ..., 0. , 0. ,
0. ]])
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, Y,test_size=0.2)
# Naive Bayes
from sklearn.naive_bayes import GaussianNB
classifier = GaussianNB()
classifier.fit(X_train, y_train)
# Predict Class
y_pred = classifier.predict(X_test)
# Accuracy
from sklearn.metrics import accuracy_score
accuracy = accuracy_score(y_test, y_pred)
accuracy
0.6230526523368172
from sklearn.metrics import classification_report
print(classification_report(y_test, y_pred, target_names=['แตแแญแต', 'แแแแ', 'แแแญ แ แแ แแ', 'แขแแแต', 'แแแ แ แแ แแ', 'แแแฒแซ', 'nan']))
precision recall f1-score support
แตแแญแต 0.89 0.34 0.50 4106
แแแแ 0.32 0.82 0.46 754
แแแญ แ แแ แแ 0.00 0.00 0.00 1
แขแแแต 0.62 0.78 0.69 1309
แแแ แ แแ แแ 0.98 0.95 0.97 1986
แแแฒแซ 0.49 0.69 0.57 1875
nan 0.23 0.87 0.36 111
accuracy 0.62 10142
macro avg 0.50 0.64 0.51 10142
weighted avg 0.75 0.62 0.62 10142