-
Notifications
You must be signed in to change notification settings - Fork 0
/
emotion_predictor.py
118 lines (101 loc) · 4.36 KB
/
emotion_predictor.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
import html
import pickle
import re
import numpy as np
import pandas as pd
from keras import backend as K
from keras.models import load_model
from keras.preprocessing import sequence
class EmotionPredictor:
def __init__(self, classification, setting, use_unison_model=True):
"""
Args:
classification (str): Either 'ekman', 'plutchik', 'poms'
or 'unison'.
setting (str): Either 'mc' or 'ml'.
use_unison_model (bool): Whether to use unison model;
else use single model.
"""
if classification not in ['ekman', 'plutchik', 'poms', 'unison']:
raise ValueError('Unknown emotion classification: {}'.format(
classification))
if setting not in ['mc', 'ml']:
raise ValueError('Unknown setting: {}'.format(setting))
self.classification = classification
self.setting = setting
self.use_unison_model = use_unison_model
self.model = self._get_model()
self.embeddings_model = self._get_embeddings_model()
self.char_to_ind = self._get_char_mapping()
self.class_values = self._get_class_values()
self.max_len = self._get_max_sequence_length()
def _get_model(self):
self._loaded_model_filename = 'models/{}{}-{}.h5'.format(
'unison-' if self.use_unison_model else '',
self.classification,
self.setting,
)
return load_model(self._loaded_model_filename)
def _get_embeddings_model(self):
last_layer_output = K.function([self.model.layers[0].input,
K.learning_phase()],
[self.model.layers[-3].output])
return lambda x: last_layer_output([x, 0])[0]
@staticmethod
def _get_char_mapping():
with open('models/allowed-chars.pkl', 'rb') as f:
return pickle.load(f)
def _get_class_values(self):
if self.classification == 'ekman':
return ['Anger', 'Disgust', 'Fear', 'Joy', 'Sadness', 'Surprise']
elif self.classification == 'plutchik':
return ['Anger', 'Disgust', 'Fear', 'Joy', 'Sadness', 'Surprise',
'Trust', 'Anticipation']
elif self.classification == 'poms':
return ['Anger', 'Depression', 'Fatigue', 'Vigour', 'Tension',
'Confusion']
def _get_max_sequence_length(self):
if self.use_unison_model or self.classification == 'poms':
return 143
elif self.classification in ['ekman', 'plutchik']:
return 141
def predict_classes(self, tweets):
indices = self._tweet_to_indices(tweets)
predictions = self.model.predict(indices, verbose=False)
df = pd.DataFrame({'Tweet': tweets})
if self.setting == 'mc':
df['Emotion'] = [self.class_values[i] for i in
predictions.argmax(axis=-1)]
else:
predictions[predictions >= 0.5] = 1
predictions[predictions < 0.5] = 0
for emotion, values in zip(self.class_values, predictions.T):
df[emotion] = values
return df
def predict_probabilities(self, tweet):
indice = self._tweet_to_indices(tweet)
#indice = np.array(indice)
prediction = self.model.predict(indice, verbose=False)
#df = pd.DataFrame({'Tweet': tweets})
df = {}
for emotion, values in zip(self.class_values, prediction.T):
df[emotion] = values[0]
return df
def embed(self, tweets):
indices = self._tweet_to_indices(tweets)
embeddings = self.embeddings_model(indices)
df = pd.DataFrame({'Tweet': tweets})
for index, values in enumerate(embeddings.T, start=1):
df['Dim{}'.format(index)] = values
return df
def embedd(self, tweets):
""" Here only for backwards compatibility. """
return self.embed(tweets)
def _tweet_to_indices(self, t):
indices = []
t = html.unescape(t) # unescape HTML
t = re.sub(r"http\S+", "", t) # remove normal URLS
t = re.sub(r"pic\.twitter\.com/\S+", "", t) # remove pic.twitter.com URLS
indice = [[self.char_to_ind[char] for char in t]]
return sequence.pad_sequences(indice, maxlen=self.max_len)
#return indice