PerSent 0.1.8__tar.gz → 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of PerSent might be problematic. Click here for more details.
- {persent-0.1.8 → persent-0.2.0}/PKG-INFO +1 -1
- persent-0.2.0/PerSent/CommentAnalyzer.py +113 -0
- persent-0.2.0/PerSent/__init__.py +4 -0
- {persent-0.1.8 → persent-0.2.0}/PerSent.egg-info/PKG-INFO +1 -1
- {persent-0.1.8 → persent-0.2.0}/PerSent.egg-info/SOURCES.txt +2 -0
- persent-0.2.0/PerSent.egg-info/top_level.txt +1 -0
- {persent-0.1.8 → persent-0.2.0}/setup.py +3 -3
- persent-0.1.8/PerSent.egg-info/top_level.txt +0 -1
- {persent-0.1.8 → persent-0.2.0}/PerSent.egg-info/dependency_links.txt +0 -0
- {persent-0.1.8 → persent-0.2.0}/PerSent.egg-info/requires.txt +0 -0
- {persent-0.1.8 → persent-0.2.0}/README.md +0 -0
- {persent-0.1.8 → persent-0.2.0}/setup.cfg +0 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#import necessary library
|
|
2
|
+
import pandas as pd
|
|
3
|
+
from hazm import Normalizer, word_tokenize, Stemmer, stopwords_list
|
|
4
|
+
import re
|
|
5
|
+
from tqdm import tqdm
|
|
6
|
+
from gensim.models import Word2Vec
|
|
7
|
+
import numpy as np
|
|
8
|
+
from sklearn.linear_model import LogisticRegression
|
|
9
|
+
from sklearn.model_selection import train_test_split
|
|
10
|
+
import os
|
|
11
|
+
import joblib
|
|
12
|
+
|
|
13
|
+
class CommentAnalyzer:
|
|
14
|
+
def __init__(self, model_dir='PerSent/model'):
|
|
15
|
+
self.normalizer = Normalizer()
|
|
16
|
+
self.stemmer = Stemmer()
|
|
17
|
+
self.stopwords = set(stopwords_list())
|
|
18
|
+
self.model_dir = model_dir
|
|
19
|
+
self.vectorizer = None
|
|
20
|
+
self.classifier = None
|
|
21
|
+
|
|
22
|
+
# make /model Directory if not exist
|
|
23
|
+
os.makedirs(self.model_dir, exist_ok=True)
|
|
24
|
+
|
|
25
|
+
def _preprocess_text(self, text):
|
|
26
|
+
"""PreProcess Persian Text"""
|
|
27
|
+
# Normalizing
|
|
28
|
+
text = self.normalizer.normalize(str(text))
|
|
29
|
+
|
|
30
|
+
# remove number and sign
|
|
31
|
+
text = re.sub(r'[!()-\[\]{};:\'",؟<>./?@#$%^&*_~۰-۹\d]+', ' ', text)
|
|
32
|
+
text = re.sub(r'\s+', ' ', text).strip()
|
|
33
|
+
|
|
34
|
+
# tokenize and stemming
|
|
35
|
+
tokens = word_tokenize(text)
|
|
36
|
+
processed_tokens = [
|
|
37
|
+
self.stemmer.stem(token)
|
|
38
|
+
for token in tokens
|
|
39
|
+
if token not in self.stopwords and len(token) > 1
|
|
40
|
+
]
|
|
41
|
+
|
|
42
|
+
return processed_tokens
|
|
43
|
+
|
|
44
|
+
def _sentence_vector(self, sentence, model):
|
|
45
|
+
"""convert sentences to vector by word2vec model"""
|
|
46
|
+
vectors = []
|
|
47
|
+
for word in sentence:
|
|
48
|
+
try:
|
|
49
|
+
vectors.append(model.wv[word])
|
|
50
|
+
except KeyError:
|
|
51
|
+
vectors.append(np.zeros(100))
|
|
52
|
+
return np.mean(vectors, axis=0) if vectors else np.zeros(100)
|
|
53
|
+
|
|
54
|
+
def train(self, train_csv, test_size=0.2, vector_size=100, window=5):
|
|
55
|
+
"""Train model"""
|
|
56
|
+
# read data
|
|
57
|
+
df = pd.read_csv(train_csv)
|
|
58
|
+
df['tokens'] = df['body'].apply(self._preprocess_text)
|
|
59
|
+
|
|
60
|
+
# train Word2Vec model
|
|
61
|
+
self.vectorizer = Word2Vec(
|
|
62
|
+
sentences=df['tokens'],
|
|
63
|
+
vector_size=vector_size,
|
|
64
|
+
window=window,
|
|
65
|
+
min_count=1,
|
|
66
|
+
workers=4
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# convert sentences to vector
|
|
70
|
+
X = np.array([self._sentence_vector(s, self.vectorizer) for s in df['tokens']])
|
|
71
|
+
y = df['recommendation_status'].map({
|
|
72
|
+
"no_idea": 2,
|
|
73
|
+
"recommended": 1,
|
|
74
|
+
"not_recommended": 0
|
|
75
|
+
}).values
|
|
76
|
+
|
|
77
|
+
# make train and test data
|
|
78
|
+
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=test_size)
|
|
79
|
+
|
|
80
|
+
self.classifier = LogisticRegression(max_iter=1000)
|
|
81
|
+
self.classifier.fit(X_train, y_train)
|
|
82
|
+
|
|
83
|
+
# save model
|
|
84
|
+
self.save_model()
|
|
85
|
+
|
|
86
|
+
# evaluation
|
|
87
|
+
accuracy = self.classifier.score(X_test, y_test)
|
|
88
|
+
return accuracy
|
|
89
|
+
|
|
90
|
+
def predict(self, text):
|
|
91
|
+
"""Predict text sentiment"""
|
|
92
|
+
if not self.classifier or not self.vectorizer:
|
|
93
|
+
raise Exception("Model not trained! Call train() first or load a pretrained model.")
|
|
94
|
+
|
|
95
|
+
tokens = self._preprocess_text(text)
|
|
96
|
+
vector = self._sentence_vector(tokens, self.vectorizer)
|
|
97
|
+
prediction = self.classifier.predict([vector])[0]
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
0: "not_recommended",
|
|
101
|
+
1: "recommended",
|
|
102
|
+
2: "no_idea"
|
|
103
|
+
}[prediction]
|
|
104
|
+
|
|
105
|
+
def save_model(self):
|
|
106
|
+
"""save trained model"""
|
|
107
|
+
joblib.dump(self.classifier, os.path.join(self.model_dir, 'classifier.joblib'))
|
|
108
|
+
self.vectorizer.save(os.path.join(self.model_dir, 'word2vec.model'))
|
|
109
|
+
|
|
110
|
+
def load_model(self):
|
|
111
|
+
"""reload from file"""
|
|
112
|
+
self.classifier = joblib.load(os.path.join(self.model_dir, 'classifier.joblib'))
|
|
113
|
+
self.vectorizer = Word2Vec.load(os.path.join(self.model_dir, 'word2vec.model'))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
PerSent
|
|
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
|
|
|
5
5
|
|
|
6
6
|
setup(
|
|
7
7
|
name="PerSent",
|
|
8
|
-
version="0.
|
|
8
|
+
version="0.2.0",
|
|
9
9
|
author="RezaGooner",
|
|
10
10
|
author_email="RezaAsadiProgrammer@Gmail.com",
|
|
11
11
|
description="Persian Sentiment Analysis Toolkit",
|
|
@@ -14,7 +14,7 @@ setup(
|
|
|
14
14
|
url="https://github.com/RezaGooner/PerSent",
|
|
15
15
|
packages=find_packages(),
|
|
16
16
|
package_data={
|
|
17
|
-
'
|
|
17
|
+
'PerSent': ['CommentAnalyzer.py', 'model/*'],
|
|
18
18
|
},
|
|
19
19
|
install_requires=[
|
|
20
20
|
'hazm>=0.7.0',
|
|
@@ -31,4 +31,4 @@ setup(
|
|
|
31
31
|
],
|
|
32
32
|
python_requires='>=3.8',
|
|
33
33
|
keywords='persian sentiment analysis nlp',
|
|
34
|
-
)
|
|
34
|
+
)
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|