tensarflows 0.1.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.
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: tensarflows
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A Python package containing GAN and RNN-LSTM source code
|
|
5
|
+
Requires-Python: >=3.9
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
|
|
8
|
+
# Tensarflows
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
pip install tensarflows
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "tensarflows"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A Python package containing GAN and RNN-LSTM source code"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.9"
|
|
11
|
+
dependencies = []
|
|
12
|
+
|
|
13
|
+
[tool.hatch.build.targets.wheel]
|
|
14
|
+
packages = ["src/tensarflows"]
|
|
15
|
+
|
|
16
|
+
[tool.hatch.build.targets.sdist]
|
|
17
|
+
only-include = [
|
|
18
|
+
"src/tensarflows",
|
|
19
|
+
"README.md",
|
|
20
|
+
"pyproject.toml"
|
|
21
|
+
]
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import tensorflow as tf
|
|
2
|
+
from tensorflow.keras import layers
|
|
3
|
+
import matplotlib.pyplot as plt
|
|
4
|
+
|
|
5
|
+
# 1. Load MNIST dataset
|
|
6
|
+
(x_train, _), _ = tf.keras.datasets.mnist.load_data()
|
|
7
|
+
|
|
8
|
+
# folder = input("Enter image folder path: ")
|
|
9
|
+
# images = []
|
|
10
|
+
|
|
11
|
+
# for file in os.listdir(folder):
|
|
12
|
+
# path = os.path.join(folder, file)
|
|
13
|
+
# img = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
|
|
14
|
+
|
|
15
|
+
# if img is not None:
|
|
16
|
+
# img = cv2.resize(img, (28, 28))
|
|
17
|
+
# images.append(img)
|
|
18
|
+
|
|
19
|
+
# x_train = np.array(images)
|
|
20
|
+
# Normalize pixel values from 0-255 to -1 to 1
|
|
21
|
+
x_train = (x_train - 127.5) / 127.5
|
|
22
|
+
|
|
23
|
+
# Convert each 28x28 image into 784 values
|
|
24
|
+
x_train = x_train.reshape(-1, 784)
|
|
25
|
+
|
|
26
|
+
# 2. Create Generator
|
|
27
|
+
G = tf.keras.Sequential([
|
|
28
|
+
layers.Dense(256, activation='relu', input_shape=(100,)),
|
|
29
|
+
layers.Dense(784, activation='tanh')
|
|
30
|
+
])
|
|
31
|
+
|
|
32
|
+
# 3. Create Discriminator
|
|
33
|
+
D = tf.keras.Sequential([
|
|
34
|
+
layers.Dense(256, activation='relu', input_shape=(784,)),
|
|
35
|
+
layers.Dense(1, activation='sigmoid')
|
|
36
|
+
])
|
|
37
|
+
|
|
38
|
+
# 4. Loss function and optimizers
|
|
39
|
+
loss = tf.keras.losses.BinaryCrossentropy()
|
|
40
|
+
|
|
41
|
+
G_optimizer = tf.keras.optimizers.Adam(0.0002)
|
|
42
|
+
D_optimizer = tf.keras.optimizers.Adam(0.0002)
|
|
43
|
+
|
|
44
|
+
# 5. Train the GAN
|
|
45
|
+
for epoch in range(10):
|
|
46
|
+
|
|
47
|
+
for i in range(0, len(x_train), 128):
|
|
48
|
+
|
|
49
|
+
# Get a batch of real images
|
|
50
|
+
real = x_train[i:i+128]
|
|
51
|
+
n = len(real)
|
|
52
|
+
|
|
53
|
+
# Generate random noise
|
|
54
|
+
noise = tf.random.normal((n, 100))
|
|
55
|
+
|
|
56
|
+
# Generate fake images
|
|
57
|
+
fake = G(noise)
|
|
58
|
+
|
|
59
|
+
# Train Discriminator
|
|
60
|
+
with tf.GradientTape() as tape:
|
|
61
|
+
real_output = D(real)
|
|
62
|
+
fake_output = D(tf.stop_gradient(fake))
|
|
63
|
+
|
|
64
|
+
real_loss = loss(tf.ones_like(real_output), real_output)
|
|
65
|
+
fake_loss = loss(tf.zeros_like(fake_output), fake_output)
|
|
66
|
+
|
|
67
|
+
D_loss = real_loss + fake_loss
|
|
68
|
+
|
|
69
|
+
gradients = tape.gradient(D_loss, D.trainable_variables)
|
|
70
|
+
D_optimizer.apply_gradients(
|
|
71
|
+
zip(gradients, D.trainable_variables)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Train Generator
|
|
75
|
+
noise = tf.random.normal((n, 100))
|
|
76
|
+
|
|
77
|
+
with tf.GradientTape() as tape:
|
|
78
|
+
fake = G(noise)
|
|
79
|
+
output = D(fake)
|
|
80
|
+
|
|
81
|
+
G_loss = loss(tf.ones_like(output), output)
|
|
82
|
+
|
|
83
|
+
gradients = tape.gradient(G_loss, G.trainable_variables)
|
|
84
|
+
G_optimizer.apply_gradients(
|
|
85
|
+
zip(gradients, G.trainable_variables)
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
print("Epoch:", epoch + 1,
|
|
89
|
+
"D Loss:", float(D_loss),
|
|
90
|
+
"G Loss:", float(G_loss))
|
|
91
|
+
|
|
92
|
+
# 6. Generate new images
|
|
93
|
+
noise = tf.random.normal((16, 100))
|
|
94
|
+
images = G(noise, training=False).numpy()
|
|
95
|
+
|
|
96
|
+
# Convert images into 28x28 format
|
|
97
|
+
images = images.reshape(16, 28, 28)
|
|
98
|
+
|
|
99
|
+
# Display generated images
|
|
100
|
+
plt.figure(figsize=(6, 6))
|
|
101
|
+
|
|
102
|
+
for i in range(16):
|
|
103
|
+
plt.subplot(4, 4, i + 1)
|
|
104
|
+
plt.imshow(images[i], cmap='gray')
|
|
105
|
+
plt.axis('off')
|
|
106
|
+
|
|
107
|
+
plt.show()
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
|
|
3
|
+
__version__ = "0.1.0"
|
|
4
|
+
|
|
5
|
+
PACKAGE_DIR = Path(__file__).parent
|
|
6
|
+
|
|
7
|
+
def print_source(filename):
|
|
8
|
+
"""Print the complete source code of a package module."""
|
|
9
|
+
allowed_files = {"GAN.py", "rnn_lstm.py"}
|
|
10
|
+
|
|
11
|
+
if filename not in allowed_files:
|
|
12
|
+
raise ValueError(f"Unsupported file: {filename}")
|
|
13
|
+
|
|
14
|
+
file_path = PACKAGE_DIR / filename
|
|
15
|
+
|
|
16
|
+
print(f"\n{'=' * 20} {filename} {'=' * 20}\n")
|
|
17
|
+
print(file_path.read_text(encoding="utf-8"))
|
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import tensorflow as tf
|
|
3
|
+
from tensorflow.keras import layers, models
|
|
4
|
+
# ---------------------------------------------------------------
|
|
5
|
+
# 1. Load the dataset
|
|
6
|
+
# ---------------------------------------------------------------
|
|
7
|
+
path = tf.keras.utils.get_file(
|
|
8
|
+
"shakespeare.txt",
|
|
9
|
+
"https://storage.googleapis.com/download.tensorflow.org/data/shakespeare.txt",
|
|
10
|
+
)
|
|
11
|
+
text = open(path, "rb").read().decode("utf-8")
|
|
12
|
+
|
|
13
|
+
MAX_CHARS = None # e.g. 300_000 for a faster, lower-quality run on CPU
|
|
14
|
+
if MAX_CHARS:
|
|
15
|
+
text = text[:MAX_CHARS]
|
|
16
|
+
|
|
17
|
+
print("Total characters:", len(text))
|
|
18
|
+
print(text[:200])
|
|
19
|
+
# ---------------------------------------------------------------
|
|
20
|
+
# 2. Vocabulary: character <-> integer
|
|
21
|
+
# ---------------------------------------------------------------
|
|
22
|
+
vocab = sorted(set(text))
|
|
23
|
+
char2idx = {c: i for i, c in enumerate(vocab)}
|
|
24
|
+
idx2char = np.array(vocab)
|
|
25
|
+
vocab_size = len(vocab)
|
|
26
|
+
print("Unique characters:", vocab_size)
|
|
27
|
+
|
|
28
|
+
data = np.array([char2idx[c] for c in text], dtype="int32")
|
|
29
|
+
|
|
30
|
+
# ---------------------------------------------------------------
|
|
31
|
+
# 3. Build training data: input = 100 chars, target = same 100 chars
|
|
32
|
+
# shifted by one (the model predicts the next char at EVERY step)
|
|
33
|
+
# ---------------------------------------------------------------
|
|
34
|
+
SEQ_LEN = 100
|
|
35
|
+
BATCH_SIZE = 64
|
|
36
|
+
|
|
37
|
+
ds = tf.data.Dataset.from_tensor_slices(data)
|
|
38
|
+
sequences = ds.batch(SEQ_LEN + 1, drop_remainder=True)
|
|
39
|
+
dataset = (
|
|
40
|
+
sequences.map(lambda s: (s[:-1], s[1:]))
|
|
41
|
+
.shuffle(10_000)
|
|
42
|
+
.batch(BATCH_SIZE, drop_remainder=True)
|
|
43
|
+
.prefetch(tf.data.AUTOTUNE)
|
|
44
|
+
)
|
|
45
|
+
# # ---------------------------------------------------------------
|
|
46
|
+
# 4. Model: Embedding -> RNN (return_sequences) -> Dense
|
|
47
|
+
# ---------------------------------------------------------------
|
|
48
|
+
model = models.Sequential([
|
|
49
|
+
layers.Input(shape=(None,)),
|
|
50
|
+
layers.Embedding(vocab_size, 128),
|
|
51
|
+
layers.SimpleRNN(256, return_sequences=True),
|
|
52
|
+
layers.Dense(vocab_size), # raw logits (no softmax)
|
|
53
|
+
])
|
|
54
|
+
|
|
55
|
+
model.compile(
|
|
56
|
+
optimizer="adam",
|
|
57
|
+
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
|
|
58
|
+
)
|
|
59
|
+
model.summary()
|
|
60
|
+
|
|
61
|
+
# ---------------------------------------------------------------
|
|
62
|
+
# 5. Train (aim for loss around 1.4 - 1.6)
|
|
63
|
+
# ---------------------------------------------------------------
|
|
64
|
+
EPOCHS = 15
|
|
65
|
+
model.fit(dataset, epochs=EPOCHS)
|
|
66
|
+
# ---------------------------------------------------------------
|
|
67
|
+
# 6. Generate text, one character at a time
|
|
68
|
+
# ---------------------------------------------------------------
|
|
69
|
+
def generate(seed, num_chars=500, temperature=0.8):
|
|
70
|
+
"""
|
|
71
|
+
temperature < 1.0 : safer, more predictable text
|
|
72
|
+
temperature > 1.0 : more random / creative, more mistakes
|
|
73
|
+
"""
|
|
74
|
+
ids = [char2idx.get(c, 0) for c in seed]
|
|
75
|
+
for _ in range(num_chars):
|
|
76
|
+
x = np.array([ids[-SEQ_LEN:]])
|
|
77
|
+
logits = model(x, training=False)[0, -1] / temperature
|
|
78
|
+
next_id = tf.random.categorical(logits[None, :], 1)[0, 0].numpy()
|
|
79
|
+
ids.append(int(next_id))
|
|
80
|
+
return "".join(idx2char[i] for i in ids)
|
|
81
|
+
|
|
82
|
+
1
|
|
83
|
+
print("\n=== Text generation ===")
|
|
84
|
+
print(generate("ROMEO: ", num_chars=500, temperature=0.8))
|
|
85
|
+
# Machine translation
|
|
86
|
+
"""
|
|
87
|
+
RNN applications in TensorFlow / Keras
|
|
88
|
+
1. Text generation (many-to-one, character-level LSTM)
|
|
89
|
+
2. Machine translation (many-to-many, encoder-decoder seq2seq LSTM)
|
|
90
|
+
3. POS tagging (many-to-many, Bidirectional LSTM)
|
|
91
|
+
|
|
92
|
+
Toy datasets are used so every part runs quickly. Replace them with real data.
|
|
93
|
+
Requires: pip install tensorflow numpy
|
|
94
|
+
"""
|
|
95
|
+
|
|
96
|
+
# =====================================================================
|
|
97
|
+
# 2. MACHINE TRANSLATION (encoder-decoder seq2seq, English -> French)
|
|
98
|
+
# =====================================================================
|
|
99
|
+
def machine_translation_demo():
|
|
100
|
+
pairs = [
|
|
101
|
+
("hello", "bonjour"), ("thank you", "merci"), ("good night", "bonne nuit"),
|
|
102
|
+
("good morning", "bonjour"), ("i love you", "je t aime"),
|
|
103
|
+
("how are you", "comment ca va"), ("see you", "a bientot"),
|
|
104
|
+
("yes", "oui"), ("no", "non"), ("please", "s il vous plait"),
|
|
105
|
+
]
|
|
106
|
+
src_texts = [p[0] for p in pairs]
|
|
107
|
+
tgt_texts = ["\t" + p[1] + "\n" for p in pairs] # \t = <start>, \n = <end>
|
|
108
|
+
|
|
109
|
+
# --- character vocabularies (index 0 reserved for padding) ---
|
|
110
|
+
src_chars = sorted(set("".join(src_texts)))
|
|
111
|
+
tgt_chars = sorted(set("".join(tgt_texts)))
|
|
112
|
+
src2idx = {c: i + 1 for i, c in enumerate(src_chars)}
|
|
113
|
+
tgt2idx = {c: i + 1 for i, c in enumerate(tgt_chars)}
|
|
114
|
+
idx2tgt = {i: c for c, i in tgt2idx.items()}
|
|
115
|
+
src_vocab, tgt_vocab = len(src2idx) + 1, len(tgt2idx) + 1
|
|
116
|
+
|
|
117
|
+
max_src = max(len(t) for t in src_texts)
|
|
118
|
+
max_tgt = max(len(t) for t in tgt_texts)
|
|
119
|
+
|
|
120
|
+
def encode(texts, vocab, maxlen):
|
|
121
|
+
arr = np.zeros((len(texts), maxlen), dtype="int32")
|
|
122
|
+
for i, t in enumerate(texts):
|
|
123
|
+
for j, c in enumerate(t):
|
|
124
|
+
arr[i, j] = vocab[c]
|
|
125
|
+
return arr
|
|
126
|
+
|
|
127
|
+
enc_in = encode(src_texts, src2idx, max_src)
|
|
128
|
+
tgt_all = encode(tgt_texts, tgt2idx, max_tgt)
|
|
129
|
+
dec_in = tgt_all[:, :-1] # teacher forcing input : <start> w1 w2 ...
|
|
130
|
+
dec_out = tgt_all[:, 1:] # expected output : w1 w2 ... <end>
|
|
131
|
+
|
|
132
|
+
latent = 128
|
|
133
|
+
|
|
134
|
+
# --- encoder ---
|
|
135
|
+
enc_inputs = layers.Input(shape=(None,))
|
|
136
|
+
enc_emb = layers.Embedding(src_vocab, 64, mask_zero=True)(enc_inputs)
|
|
137
|
+
_, state_h, state_c = layers.LSTM(latent, return_state=True)(enc_emb)
|
|
138
|
+
enc_states = [state_h, state_c]
|
|
139
|
+
|
|
140
|
+
# --- decoder (initial state = encoder's final state) ---
|
|
141
|
+
dec_inputs = layers.Input(shape=(None,))
|
|
142
|
+
dec_emb_layer = layers.Embedding(tgt_vocab, 64, mask_zero=True)
|
|
143
|
+
dec_lstm = layers.LSTM(latent, return_sequences=True, return_state=True)
|
|
144
|
+
dec_dense = layers.Dense(tgt_vocab, activation="softmax")
|
|
145
|
+
|
|
146
|
+
dec_seq, _, _ = dec_lstm(dec_emb_layer(dec_inputs), initial_state=enc_states)
|
|
147
|
+
dec_probs = dec_dense(dec_seq)
|
|
148
|
+
|
|
149
|
+
# --- training model ---
|
|
150
|
+
model = models.Model([enc_inputs, dec_inputs], dec_probs)
|
|
151
|
+
model.compile(optimizer="adam", loss="sparse_categorical_crossentropy")
|
|
152
|
+
model.fit([enc_in, dec_in], dec_out, batch_size=4, epochs=300, verbose=1)
|
|
153
|
+
|
|
154
|
+
# --- inference models (reuse trained layers) ---
|
|
155
|
+
encoder_model = models.Model(enc_inputs, enc_states)
|
|
156
|
+
|
|
157
|
+
h_in = layers.Input(shape=(latent,))
|
|
158
|
+
c_in = layers.Input(shape=(latent,))
|
|
159
|
+
out, h, c = dec_lstm(dec_emb_layer(dec_inputs), initial_state=[h_in, c_in])
|
|
160
|
+
decoder_model = models.Model([dec_inputs, h_in, c_in],
|
|
161
|
+
[dec_dense(out), h, c])
|
|
162
|
+
|
|
163
|
+
def translate(sentence):
|
|
164
|
+
x = encode([sentence], src2idx, max_src)
|
|
165
|
+
h, c = encoder_model.predict(x, verbose=0)
|
|
166
|
+
token = np.array([[tgt2idx["\t"]]])
|
|
167
|
+
result = ""
|
|
168
|
+
for _ in range(max_tgt):
|
|
169
|
+
probs, h, c = decoder_model.predict([token, h, c], verbose=0)
|
|
170
|
+
idx = int(np.argmax(probs[0, -1])) # greedy decoding
|
|
171
|
+
ch = idx2tgt.get(idx, "")
|
|
172
|
+
if ch == "\n":
|
|
173
|
+
break
|
|
174
|
+
result += ch
|
|
175
|
+
token = np.array([[idx]])
|
|
176
|
+
return result
|
|
177
|
+
|
|
178
|
+
print("=== Machine translation ===")
|
|
179
|
+
for s in ["hello", "thank you", "good night", "i love you"]:
|
|
180
|
+
print(f"{s} -> {translate(s)}")
|
|
181
|
+
machine_translation_demo()
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# =====================================================================
|
|
186
|
+
# 3. POS TAGGING (Bidirectional LSTM, many-to-many)
|
|
187
|
+
# =====================================================================
|
|
188
|
+
def pos_tagging_demo():
|
|
189
|
+
data = [
|
|
190
|
+
[("the", "DET"), ("dog", "NOUN"), ("barks", "VERB")],
|
|
191
|
+
[("a", "DET"), ("cat", "NOUN"), ("sleeps", "VERB")],
|
|
192
|
+
[("the", "DET"), ("big", "ADJ"), ("dog", "NOUN"), ("runs", "VERB")],
|
|
193
|
+
[("she", "PRON"), ("reads", "VERB"), ("a", "DET"), ("book", "NOUN")],
|
|
194
|
+
[("he", "PRON"), ("eats", "VERB"), ("the", "DET"), ("red", "ADJ"), ("apple", "NOUN")],
|
|
195
|
+
[("the", "DET"), ("small", "ADJ"), ("cat", "NOUN"), ("jumps", "VERB")],
|
|
196
|
+
[("a", "DET"), ("bird", "NOUN"), ("sings", "VERB")],
|
|
197
|
+
]
|
|
198
|
+
|
|
199
|
+
# --- vocabularies (0 = PAD, 1 = UNK for words) ---
|
|
200
|
+
words = sorted({w for s in data for w, _ in s})
|
|
201
|
+
tags = sorted({t for s in data for _, t in s})
|
|
202
|
+
word2idx = {w: i + 2 for i, w in enumerate(words)}
|
|
203
|
+
word2idx["<PAD>"], word2idx["<UNK>"] = 0, 1
|
|
204
|
+
tag2idx = {t: i + 1 for i, t in enumerate(tags)}
|
|
205
|
+
tag2idx["<PAD>"] = 0
|
|
206
|
+
idx2tag = {i: t for t, i in tag2idx.items()}
|
|
207
|
+
|
|
208
|
+
max_len = max(len(s) for s in data)
|
|
209
|
+
X = np.zeros((len(data), max_len), dtype="int32")
|
|
210
|
+
y = np.zeros((len(data), max_len), dtype="int32")
|
|
211
|
+
for i, sent in enumerate(data):
|
|
212
|
+
for j, (w, t) in enumerate(sent):
|
|
213
|
+
X[i, j] = word2idx[w]
|
|
214
|
+
y[i, j] = tag2idx[t]
|
|
215
|
+
|
|
216
|
+
# --- model: Embedding -> BiLSTM (return_sequences) -> Dense per time step ---
|
|
217
|
+
model = models.Sequential([
|
|
218
|
+
layers.Input(shape=(max_len,)),
|
|
219
|
+
layers.Embedding(len(word2idx), 50, mask_zero=True),
|
|
220
|
+
layers.Bidirectional(layers.LSTM(64, return_sequences=True)),
|
|
221
|
+
layers.TimeDistributed(layers.Dense(len(tag2idx), activation="softmax")),
|
|
222
|
+
])
|
|
223
|
+
model.compile(optimizer="adam", loss="sparse_categorical_crossentropy",
|
|
224
|
+
metrics=["accuracy"])
|
|
225
|
+
model.fit(X, y, epochs=100, batch_size=2, verbose=0)
|
|
226
|
+
|
|
227
|
+
def tag_sentence(sentence):
|
|
228
|
+
toks = sentence.lower().split()
|
|
229
|
+
x = np.zeros((1, max_len), dtype="int32")
|
|
230
|
+
for j, w in enumerate(toks[:max_len]):
|
|
231
|
+
x[0, j] = word2idx.get(w, 1)
|
|
232
|
+
pred = np.argmax(model.predict(x, verbose=0)[0], axis=-1)
|
|
233
|
+
return [(w, idx2tag[p]) for w, p in zip(toks, pred)]
|
|
234
|
+
|
|
235
|
+
print("=== POS tagging ===")
|
|
236
|
+
print(tag_sentence("the dog sleeps"))
|
|
237
|
+
print(tag_sentence("she eats a red apple"))
|
|
238
|
+
pos_tagging_demo()
|
|
239
|
+
# Sentiment analysis with an RNN (LSTM) in TensorFlow / Keras
|
|
240
|
+
"""
|
|
241
|
+
Sentiment analysis with an RNN (LSTM) in TensorFlow / Keras
|
|
242
|
+
Dataset : IMDB movie reviews (50,000 reviews, positive / negative)
|
|
243
|
+
Type : many-to-one (sequence of words -> single label)
|
|
244
|
+
Requires: pip install tensorflow numpy
|
|
245
|
+
"""
|
|
246
|
+
import numpy as np
|
|
247
|
+
import tensorflow as tf
|
|
248
|
+
from tensorflow.keras import layers, models
|
|
249
|
+
from tensorflow.keras.datasets import imdb
|
|
250
|
+
from tensorflow.keras.preprocessing.sequence import pad_sequences
|
|
251
|
+
|
|
252
|
+
# ---------------------------------------------------------------
|
|
253
|
+
# 1. Load data (reviews are already converted to word indices)
|
|
254
|
+
# ---------------------------------------------------------------
|
|
255
|
+
VOCAB_SIZE = 10000 # keep the 10,000 most frequent words
|
|
256
|
+
MAX_LEN = 200 # pad / truncate every review to 200 words
|
|
257
|
+
|
|
258
|
+
(x_train, y_train), (x_test, y_test) = imdb.load_data(num_words=VOCAB_SIZE)
|
|
259
|
+
|
|
260
|
+
x_train = pad_sequences(x_train, maxlen=MAX_LEN, padding="post", truncating="post")
|
|
261
|
+
x_test = pad_sequences(x_test, maxlen=MAX_LEN, padding="post", truncating="post")
|
|
262
|
+
|
|
263
|
+
print("Train shape:", x_train.shape, "| Test shape:", x_test.shape)
|
|
264
|
+
|
|
265
|
+
# ---------------------------------------------------------------
|
|
266
|
+
# 2. Build model: Embedding -> Bidirectional LSTM -> Dense(sigmoid)
|
|
267
|
+
# ---------------------------------------------------------------
|
|
268
|
+
model = models.Sequential([
|
|
269
|
+
layers.Input(shape=(MAX_LEN,)),
|
|
270
|
+
layers.Embedding(VOCAB_SIZE, 64, mask_zero=True), # word index -> 64-d vector
|
|
271
|
+
layers.Bidirectional(layers.LSTM(64)), # reads text both directions
|
|
272
|
+
layers.Dropout(0.5), # reduces overfitting
|
|
273
|
+
layers.Dense(32, activation="relu"),
|
|
274
|
+
layers.Dense(1, activation="sigmoid"), # probability of positive
|
|
275
|
+
])
|
|
276
|
+
|
|
277
|
+
model.compile(
|
|
278
|
+
loss="binary_crossentropy",
|
|
279
|
+
optimizer="adam",
|
|
280
|
+
metrics=["accuracy"],
|
|
281
|
+
)
|
|
282
|
+
model.summary()
|
|
283
|
+
|
|
284
|
+
# ---------------------------------------------------------------
|
|
285
|
+
# 3. Train
|
|
286
|
+
# ---------------------------------------------------------------
|
|
287
|
+
early_stop = tf.keras.callbacks.EarlyStopping(
|
|
288
|
+
monitor="val_loss", patience=2, restore_best_weights=True
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
history = model.fit(
|
|
292
|
+
x_train, y_train,
|
|
293
|
+
validation_split=0.2,
|
|
294
|
+
epochs=10,
|
|
295
|
+
batch_size=128,
|
|
296
|
+
callbacks=[early_stop],
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
# ---------------------------------------------------------------
|
|
300
|
+
# 4. Evaluate
|
|
301
|
+
# ---------------------------------------------------------------
|
|
302
|
+
loss, acc = model.evaluate(x_test, y_test, verbose=0)
|
|
303
|
+
print(f"\nTest accuracy: {acc:.4f}")
|
|
304
|
+
|
|
305
|
+
# ---------------------------------------------------------------
|
|
306
|
+
# 5. Predict sentiment of your own sentences
|
|
307
|
+
# ---------------------------------------------------------------
|
|
308
|
+
word_index = imdb.get_word_index()
|
|
309
|
+
# Keras reserves 0 = pad, 1 = start, 2 = unknown, so real words are shifted by 3
|
|
310
|
+
word_index = {w: (i + 3) for w, i in word_index.items()}
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def encode_review(text):
|
|
314
|
+
tokens = [1] # start token
|
|
315
|
+
for w in text.lower().split():
|
|
316
|
+
idx = word_index.get(w, 2)
|
|
317
|
+
tokens.append(idx if idx < VOCAB_SIZE else 2)
|
|
318
|
+
return pad_sequences([tokens], maxlen=MAX_LEN, padding="post", truncating="post")
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def predict_sentiment(text):
|
|
322
|
+
prob = float(model.predict(encode_review(text), verbose=0)[0][0])
|
|
323
|
+
label = "Positive" if prob >= 0.5 else "Negative"
|
|
324
|
+
print(f"{label} ({prob:.2f}): {text}")
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
predict_sentiment("this movie was fantastic and the acting was brilliant")
|
|
328
|
+
predict_sentiment("terrible plot and very boring waste of time")
|
|
329
|
+
|
|
330
|
+
# SENTIMENT ANALYSIS - CSV INPUT FILE - MULTIPLE LABELS - SOFTMAX VERSION
|
|
331
|
+
|
|
332
|
+
import pandas as pd
|
|
333
|
+
from sklearn.model_selection import train_test_split
|
|
334
|
+
|
|
335
|
+
df = pd.read_csv("reviews.csv")
|
|
336
|
+
|
|
337
|
+
label_map = {"negative": 0, "neutral": 1, "positive": 2}
|
|
338
|
+
idx_to_label = {v: k for k, v in label_map.items()}
|
|
339
|
+
|
|
340
|
+
y = df["label"].str.lower().map(label_map).values # integers 0, 1, 2
|
|
341
|
+
texts = df["text"].astype(str).values
|
|
342
|
+
|
|
343
|
+
x_train_txt, x_test_txt, y_train, y_test = train_test_split(
|
|
344
|
+
texts, y, test_size=0.2, random_state=42, stratify=y)
|
|
345
|
+
|
|
346
|
+
# Convert raw text to padded word-index sequences
|
|
347
|
+
vectorize = layers.TextVectorization(max_tokens=VOCAB_SIZE, output_sequence_length=MAX_LEN)
|
|
348
|
+
vectorize.adapt(x_train_txt) # learn vocabulary from training text only
|
|
349
|
+
x_train = vectorize(x_train_txt).numpy()
|
|
350
|
+
x_test = vectorize(x_test_txt).numpy()
|
|
351
|
+
|
|
352
|
+
#ENCODE REVIEW IS NOT REQUIRED
|
|
353
|
+
|
|
354
|
+
def predict_sentiment(text):
|
|
355
|
+
probs = model.predict(vectorize([text]), verbose=0)[0]
|
|
356
|
+
label = idx_to_label[int(np.argmax(probs))]
|
|
357
|
+
print(f"{label} ({probs.max():.2f}): {text}")
|
|
358
|
+
|
|
359
|
+
#MODEL CHANGE IN OUTPUT LAYER - REST MODEL IS ALL SAME
|
|
360
|
+
layers.Dense(3, activation="softmax") # was: Dense(1, activation="sigmoid")
|
|
361
|
+
|
|
362
|
+
model.compile(
|
|
363
|
+
loss="sparse_categorical_crossentropy", # was: binary_crossentropy
|
|
364
|
+
optimizer="adam",
|
|
365
|
+
metrics=["accuracy"],
|
|
366
|
+
)
|