-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtrain_lstm_sentiment.py
More file actions
176 lines (158 loc) · 5.45 KB
/
Copy pathtrain_lstm_sentiment.py
File metadata and controls
176 lines (158 loc) · 5.45 KB
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import os
import json
import pandas as pd
import numpy as np
import tensorflow as tf
from tensorflow.keras.layers import Embedding, LSTM, Dense, Dropout, Bidirectional
from tensorflow.keras.models import Sequential
from tensorflow.keras.preprocessing.text import Tokenizer
from tensorflow.keras.preprocessing.sequence import pad_sequences
# ---- CONFIG ----
DATA_DIR = "sentiment/data"
MODEL_OUT = "outputs/sentiment/saved_model"
VOCAB_SIZE = 10000 # you can tune this
MAX_LEN = 64 # you can tune this
EMBEDDING_DIM = 64
BATCH_SIZE = 32
EPOCHS = 6
# ---- DATA LOAD ----
def load_data(filename):
df = pd.read_csv(filename)
texts = df['review'].astype(str).tolist()
labels = df['label'].astype(int).tolist()
return texts, labels
train_texts, train_labels = load_data(os.path.join(DATA_DIR, "train.csv"))
val_texts, val_labels = load_data(os.path.join(DATA_DIR, "val.csv"))
test_texts, test_labels = load_data(os.path.join(DATA_DIR, "test.csv"))
# --- DATA AUGMENTATION: Add clear positive/negative sentences ---
custom_sentences = [
# NEGATIVE
("i hate you", 0),
("i hate this", 0),
("i hate it", 0),
("hate this", 0),
("hate it", 0),
("i dislike this", 0),
("this is terrible", 0),
("worst movie ever", 0),
("this is bad", 0),
("so bad", 0),
("awful experience", 0),
("this made me angry", 0),
("absolutely horrible", 0),
("i'm very disappointed", 0),
("i can't stand this", 0),
("i am sad", 0),
("i feel sad", 0),
("this sucks", 0),
("not good", 0),
("do not recommend", 0),
("waste of time", 0),
("what a letdown", 0),
("very boring", 0),
("i regret watching", 0),
("such a bad movie", 0),
("it was painful to watch", 0),
("never again", 0),
("so boring", 0),
("not worth it", 0),
("big disappointment", 0),
# POSITIVE
("i love you", 1),
("i love this", 1),
("i love it", 1),
("love this", 1),
("love it", 1),
("i like this", 1),
("this is awesome", 1),
("best movie ever", 1),
("this is good", 1),
("so good", 1),
("i am happy", 1),
("wonderful experience", 1),
("this made me smile", 1),
("absolutely fantastic", 1),
("i'm very impressed", 1),
("i enjoyed this", 1),
("it was great", 1),
("this is amazing", 1),
("superb", 1),
("highly recommend", 1),
("worth every minute", 1),
("what a treat", 1),
("so entertaining", 1),
("i'd watch it again", 1),
("such a good movie", 1),
("i laughed so much", 1),
("pure joy", 1),
("it made my day", 1),
("very fun", 1),
]
# Convert to DataFrame and add to train set
custom_df = pd.DataFrame(custom_sentences, columns=["review", "label"])
aug_train_texts = train_texts + custom_df["review"].tolist()
aug_train_labels = train_labels + custom_df["label"].tolist()
print(f"Augmented training set size: {len(aug_train_texts)}")
# ---- TOKENIZE ----
tokenizer = Tokenizer(num_words=VOCAB_SIZE, oov_token="<OOV>", lower=True)
tokenizer.fit_on_texts(aug_train_texts)
print("Top 20 words in tokenizer:", list(tokenizer.word_index.items())[:20])
important_words = ["i", "love", "hate", "you", "sad", "movie", "very", "this"]
for word in important_words:
print(f"Index for '{word}':", tokenizer.word_index.get(word))
print("OOV token:", tokenizer.oov_token)
print("OOV index:", tokenizer.word_index.get(tokenizer.oov_token))
print("Tokenizer vocab size (should be <= VOCAB_SIZE):", len(tokenizer.word_index))
def encode(texts):
sequences = tokenizer.texts_to_sequences(texts)
return pad_sequences(sequences, maxlen=MAX_LEN, padding='post', truncating='post')
X_train = encode(train_texts)
X_val = encode(val_texts)
X_test = encode(test_texts)
y_train = np.array(train_labels)
y_val = np.array(val_labels)
y_test = np.array(test_labels)
# ---- MODEL ----
model = Sequential([
Embedding(VOCAB_SIZE, EMBEDDING_DIM, input_length=MAX_LEN),
Bidirectional(LSTM(64, activation='tanh', recurrent_activation='sigmoid', unroll=True)),
Dropout(0.3),
Dense(32, activation='relu'),
Dropout(0.2),
Dense(1, activation='sigmoid')
])
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'])
model.summary()
# ---- TRAIN ----
callbacks = [
tf.keras.callbacks.EarlyStopping(patience=2, restore_best_weights=True)
]
history = model.fit(
X_train, y_train,
validation_data=(X_val, y_val),
batch_size=BATCH_SIZE,
epochs=EPOCHS,
callbacks=callbacks
)
# ---- EVALUATE ----
loss, acc = model.evaluate(X_test, y_test, batch_size=BATCH_SIZE)
print(f"Test accuracy: {acc:.4f}")
# ---- EXPORT ----
os.makedirs(MODEL_OUT, exist_ok=True)
# Save Keras model (.keras) for Python
keras_path = os.path.join("outputs/sentiment", "sentiment_model.keras")
model.save(keras_path)
print(f"Keras model saved at {keras_path}")
# Export for TensorFlow.js (SavedModel format)
model.export(MODEL_OUT)
print(f"Model exported to {MODEL_OUT}")
# Save tokenizer config (for reference/legacy)
tokenizer_config_path = os.path.join(MODEL_OUT, "tokenizer_config.json")
with open(tokenizer_config_path, "w") as f:
f.write(tokenizer.to_json())
print(f"Tokenizer config saved at {tokenizer_config_path}")
# Save word_index as clean JSON for JS/Python
word_index_path = os.path.join(MODEL_OUT, "word_index.json")
with open(word_index_path, "w") as f:
json.dump(tokenizer.word_index, f)
print(f"Word index saved at {word_index_path}")