libz 0.5__tar.gz → 0.7__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.
- {libz-0.5 → libz-0.7}/PKG-INFO +1 -1
- libz-0.7/libz/DL/prac8.py +73 -0
- libz-0.7/libz/DL/prac9.py +73 -0
- {libz-0.5 → libz-0.7}/libz/DLG/__init__.py +16 -15
- libz-0.7/libz/DLG/prac1.py +145 -0
- libz-0.7/libz/DLG/prac2.py +180 -0
- libz-0.7/libz/DLG/prac3.py +196 -0
- libz-0.7/libz/DLG/prac4.py +177 -0
- libz-0.7/libz/DLG/prac5.py +269 -0
- libz-0.7/libz/DLG/prac6.py +253 -0
- libz-0.7/libz/DLG/prac7.py +194 -0
- {libz-0.5 → libz-0.7}/libz/__init__.py +4 -1
- {libz-0.5 → libz-0.7}/libz/_core.py +10 -3
- libz-0.7/libz/_update.py +47 -0
- {libz-0.5 → libz-0.7}/libz.egg-info/PKG-INFO +1 -1
- {libz-0.5 → libz-0.7}/libz.egg-info/SOURCES.txt +2 -3
- {libz-0.5 → libz-0.7}/pyproject.toml +1 -1
- libz-0.5/libz/DL/prac8.py +0 -114
- libz-0.5/libz/DL/prac9.py +0 -158
- libz-0.5/libz/DLG/prac1.py +0 -71
- libz-0.5/libz/DLG/prac2.py +0 -108
- libz-0.5/libz/DLG/prac3.py +0 -108
- libz-0.5/libz/DLG/prac4.py +0 -105
- libz-0.5/libz/DLG/prac5.py +0 -99
- libz-0.5/libz/DLG/prac6.py +0 -119
- libz-0.5/libz/DLG/prac7.py +0 -110
- libz-0.5/libz/DLG/prac8.py +0 -141
- libz-0.5/libz/DLG/prac9.py +0 -93
- {libz-0.5 → libz-0.7}/README.md +0 -0
- {libz-0.5 → libz-0.7}/libz/AWT/__init__.py +0 -0
- {libz-0.5 → libz-0.7}/libz/AWT/prac1.py +0 -0
- {libz-0.5 → libz-0.7}/libz/AWT/prac10.py +0 -0
- {libz-0.5 → libz-0.7}/libz/AWT/prac2.py +0 -0
- {libz-0.5 → libz-0.7}/libz/AWT/prac3.py +0 -0
- {libz-0.5 → libz-0.7}/libz/AWT/prac4.py +0 -0
- {libz-0.5 → libz-0.7}/libz/AWT/prac5.py +0 -0
- {libz-0.5 → libz-0.7}/libz/AWT/prac6.py +0 -0
- {libz-0.5 → libz-0.7}/libz/AWT/prac7.py +0 -0
- {libz-0.5 → libz-0.7}/libz/AWT/prac8.py +0 -0
- {libz-0.5 → libz-0.7}/libz/AWT/prac9.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BDA/__init__.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BDA/prac1.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BDA/prac10.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BDA/prac2.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BDA/prac3.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BDA/prac4.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BDA/prac5.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BDA/prac6.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BDA/prac7.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BDA/prac8.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BDA/prac9.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BIO/__init__.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BIO/prac1.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BIO/prac10.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BIO/prac2.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BIO/prac3.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BIO/prac4.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BIO/prac5.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BIO/prac6.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BIO/prac7.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BIO/prac8.py +0 -0
- {libz-0.5 → libz-0.7}/libz/BIO/prac9.py +0 -0
- {libz-0.5 → libz-0.7}/libz/DL/__init__.py +0 -0
- {libz-0.5 → libz-0.7}/libz/DL/prac1.py +0 -0
- {libz-0.5 → libz-0.7}/libz/DL/prac2.py +0 -0
- {libz-0.5 → libz-0.7}/libz/DL/prac3.py +0 -0
- {libz-0.5 → libz-0.7}/libz/DL/prac4.py +0 -0
- {libz-0.5 → libz-0.7}/libz/DL/prac5.py +0 -0
- {libz-0.5 → libz-0.7}/libz/DL/prac6.py +0 -0
- {libz-0.5 → libz-0.7}/libz/DL/prac7.py +0 -0
- {libz-0.5 → libz-0.7}/libz.egg-info/dependency_links.txt +0 -0
- {libz-0.5 → libz-0.7}/libz.egg-info/top_level.txt +0 -0
- {libz-0.5 → libz-0.7}/setup.cfg +0 -0
{libz-0.5 → libz-0.7}/PKG-INFO
RENAMED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Recursive Neural Networks
|
|
2
|
+
|
|
3
|
+
import torch
|
|
4
|
+
import torch.nn as nn
|
|
5
|
+
import torch.nn.functional as F
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class RecursiveNN(nn.Module):
|
|
9
|
+
|
|
10
|
+
def __init__(self, vocab_size, embed_dim, hidden_dim, num_classes):
|
|
11
|
+
super().__init__()
|
|
12
|
+
|
|
13
|
+
self.embedding = nn.Embedding(vocab_size, embed_dim)
|
|
14
|
+
self.combine = nn.Linear(embed_dim * 2, hidden_dim)
|
|
15
|
+
self.classifier = nn.Linear(hidden_dim, num_classes)
|
|
16
|
+
|
|
17
|
+
def forward(self, tree):
|
|
18
|
+
|
|
19
|
+
# Leaf node
|
|
20
|
+
if isinstance(tree, int):
|
|
21
|
+
return self.embedding(torch.tensor(tree))
|
|
22
|
+
|
|
23
|
+
# Recursive case
|
|
24
|
+
left, right = tree
|
|
25
|
+
|
|
26
|
+
left_vec = self.forward(left)
|
|
27
|
+
right_vec = self.forward(right)
|
|
28
|
+
|
|
29
|
+
combined = torch.cat([left_vec, right_vec])
|
|
30
|
+
|
|
31
|
+
return torch.tanh(self.combine(combined))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
# Parameters
|
|
35
|
+
VOCAB_SIZE = 100
|
|
36
|
+
EMBED_DIM = 16
|
|
37
|
+
HIDDEN_DIM = 16
|
|
38
|
+
NUM_CLASSES = 2
|
|
39
|
+
|
|
40
|
+
# Create model
|
|
41
|
+
model = RecursiveNN(
|
|
42
|
+
VOCAB_SIZE,
|
|
43
|
+
EMBED_DIM,
|
|
44
|
+
HIDDEN_DIM,
|
|
45
|
+
NUM_CLASSES
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
# Word IDs
|
|
49
|
+
vocab = {
|
|
50
|
+
"the": 10,
|
|
51
|
+
"movie": 25,
|
|
52
|
+
"was": 4,
|
|
53
|
+
"great": 82
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Tree: ((the, movie), (was, great))
|
|
57
|
+
tree = (
|
|
58
|
+
(vocab["the"], vocab["movie"]),
|
|
59
|
+
(vocab["was"], vocab["great"])
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
# Get root vector
|
|
63
|
+
root = model(tree)
|
|
64
|
+
|
|
65
|
+
# Classify root
|
|
66
|
+
logits = model.classifier(root)
|
|
67
|
+
|
|
68
|
+
# Convert scores to probabilities
|
|
69
|
+
probabilities = F.softmax(logits, dim=0)
|
|
70
|
+
|
|
71
|
+
print("Root:", root)
|
|
72
|
+
print("Logits:", logits)
|
|
73
|
+
print("Probabilities:", probabilities)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Linear Factor Models
|
|
2
|
+
|
|
3
|
+
import torch
|
|
4
|
+
import torch.nn as nn
|
|
5
|
+
import torch.optim as optim
|
|
6
|
+
from sklearn.datasets import make_blobs
|
|
7
|
+
from sklearn.decomposition import PCA
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# 1. Create data
|
|
11
|
+
X, _ = make_blobs(
|
|
12
|
+
n_samples=1000,
|
|
13
|
+
n_features=10,
|
|
14
|
+
centers=3,
|
|
15
|
+
random_state=42
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
X = torch.tensor(X, dtype=torch.float32)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# 2. Linear Factor Model using PCA
|
|
22
|
+
pca = PCA(n_components=3)
|
|
23
|
+
factors = pca.fit_transform(X.numpy())
|
|
24
|
+
|
|
25
|
+
print("PCA Variance:", pca.explained_variance_ratio_)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
# 3. Linear Autoencoder
|
|
29
|
+
class LinearAutoencoder(nn.Module):
|
|
30
|
+
|
|
31
|
+
def __init__(self, input_dim, factor_dim):
|
|
32
|
+
super().__init__()
|
|
33
|
+
|
|
34
|
+
self.encoder = nn.Linear(input_dim, factor_dim)
|
|
35
|
+
self.decoder = nn.Linear(factor_dim, input_dim)
|
|
36
|
+
|
|
37
|
+
def forward(self, x):
|
|
38
|
+
factors = self.encoder(x)
|
|
39
|
+
output = self.decoder(factors)
|
|
40
|
+
|
|
41
|
+
return output, factors
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# 4. Create model
|
|
45
|
+
model = LinearAutoencoder(10, 3)
|
|
46
|
+
|
|
47
|
+
loss_fn = nn.MSELoss()
|
|
48
|
+
optimizer = optim.Adam(model.parameters(), lr=0.01)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
# 5. Train model
|
|
52
|
+
for epoch in range(200):
|
|
53
|
+
|
|
54
|
+
optimizer.zero_grad()
|
|
55
|
+
|
|
56
|
+
output, factors = model(X)
|
|
57
|
+
|
|
58
|
+
loss = loss_fn(output, X)
|
|
59
|
+
|
|
60
|
+
loss.backward()
|
|
61
|
+
optimizer.step()
|
|
62
|
+
|
|
63
|
+
if (epoch + 1) % 50 == 0:
|
|
64
|
+
print(
|
|
65
|
+
"Epoch:", epoch + 1,
|
|
66
|
+
"Loss:", loss.item()
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# 6. Get factor loading matrix
|
|
71
|
+
W = model.decoder.weight.detach()
|
|
72
|
+
|
|
73
|
+
print("Factor Loading Matrix Shape:", W.shape)
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
from libz._core import register
|
|
2
|
-
|
|
3
|
-
TITLES = {
|
|
4
|
-
1: "Deep feed forward (any CSV)",
|
|
5
|
-
2: "Regularization and optimization (any CSV)",
|
|
6
|
-
3: "Semi-supervised learning (any text CSV)",
|
|
7
|
-
4: "Multitasking (any CSV)",
|
|
8
|
-
5: "CNN-based handwriting recognition (any image folder / MNIST)",
|
|
9
|
-
6: "CNN - unsupervised feature learning (any image folder / digits)",
|
|
10
|
-
7: "Recurrent neural network (any text CSV / IMDB)",
|
|
11
|
-
8: "Recursive neural network (tree)
|
|
12
|
-
9: "PCA vs linear autoencoder
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
from libz._core import register
|
|
2
|
+
|
|
3
|
+
TITLES = {
|
|
4
|
+
1: "Deep feed forward (any CSV)",
|
|
5
|
+
2: "Regularization and optimization (any CSV)",
|
|
6
|
+
3: "Semi-supervised learning (any text CSV)",
|
|
7
|
+
4: "Multitasking (any CSV)",
|
|
8
|
+
5: "CNN-based handwriting recognition (any image folder / CSV / MNIST)",
|
|
9
|
+
6: "CNN - unsupervised feature learning (any image folder / CSV / digits)",
|
|
10
|
+
7: "Recurrent neural network (any text CSV / IMDB)",
|
|
11
|
+
8: "Recursive neural network (tree)",
|
|
12
|
+
9: "PCA vs linear autoencoder",
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
# prac8 and prac9 show the DL code (no dynamic version needed)
|
|
16
|
+
register(__name__, globals(), TITLES, links={8: "DL", 9: "DL"})
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Deep Feed Forward Neural Network - works with any CSV classification dataset
|
|
2
|
+
# The target column and the feature columns are picked automatically.
|
|
3
|
+
# If accuracy is low, you are asked which columns to use and the model is retrained.
|
|
4
|
+
# Press Enter to use the built-in Breast Cancer dataset (from scikit-learn, no download).
|
|
5
|
+
import numpy as np
|
|
6
|
+
import pandas as pd
|
|
7
|
+
from sklearn.datasets import load_breast_cancer
|
|
8
|
+
from sklearn.model_selection import train_test_split
|
|
9
|
+
from sklearn.preprocessing import StandardScaler, LabelEncoder
|
|
10
|
+
from sklearn.metrics import classification_report, confusion_matrix
|
|
11
|
+
from tensorflow.keras.models import Sequential
|
|
12
|
+
from tensorflow.keras.layers import Input, Dense, Dropout
|
|
13
|
+
from tensorflow.keras.callbacks import EarlyStopping
|
|
14
|
+
|
|
15
|
+
MIN_ACCURACY = 0.70 # below this you get to choose the columns yourself
|
|
16
|
+
TARGET_NAMES = {"target", "label", "labels", "class", "output", "outcome", "result", "y"}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ---------- helpers ----------
|
|
20
|
+
def read_csv(path):
|
|
21
|
+
"""Read a CSV with any separator (, ; tab |) and encoding."""
|
|
22
|
+
with open(path, encoding="latin-1") as f:
|
|
23
|
+
first_line = f.readline()
|
|
24
|
+
sep = max([",", ";", "\t", "|"], key=first_line.count)
|
|
25
|
+
try:
|
|
26
|
+
df = pd.read_csv(path, sep=sep)
|
|
27
|
+
except UnicodeDecodeError:
|
|
28
|
+
df = pd.read_csv(path, sep=sep, encoding="latin-1")
|
|
29
|
+
df = df.loc[:, ~df.columns.astype(str).str.startswith("Unnamed")]
|
|
30
|
+
return df.dropna(axis=1, how="all")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def is_id(col):
|
|
34
|
+
name = str(col.name).lower()
|
|
35
|
+
return (name in ("id", "index") or name.endswith("_id")
|
|
36
|
+
or (pd.api.types.is_integer_dtype(col) and col.nunique() == len(col)))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def guess_target(df):
|
|
40
|
+
for c in df.columns:
|
|
41
|
+
if str(c).lower() in TARGET_NAMES:
|
|
42
|
+
return c
|
|
43
|
+
return [c for c in df.columns if not is_id(df[c]) and df[c].nunique() >= 2][-1]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def make_labels(col):
|
|
47
|
+
"""Numeric columns with many values become High / Low (split at the median)."""
|
|
48
|
+
if pd.api.types.is_numeric_dtype(col) and col.nunique() > 10:
|
|
49
|
+
col = pd.Series(np.where(col >= col.median(), "High", "Low"), index=col.index)
|
|
50
|
+
encoder = LabelEncoder()
|
|
51
|
+
return encoder.fit_transform(col.astype(str)), [str(c) for c in encoder.classes_]
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def make_features(X):
|
|
55
|
+
"""Drop ID / constant / free-text columns, fill missing values, one-hot encode."""
|
|
56
|
+
keep = [c for c in X.columns
|
|
57
|
+
if not is_id(X[c]) and X[c].nunique() > 1
|
|
58
|
+
and (pd.api.types.is_numeric_dtype(X[c]) or X[c].nunique() <= 50)]
|
|
59
|
+
X = X[keep].copy()
|
|
60
|
+
for c in X.columns:
|
|
61
|
+
if pd.api.types.is_numeric_dtype(X[c]):
|
|
62
|
+
X[c] = X[c].fillna(X[c].median())
|
|
63
|
+
else:
|
|
64
|
+
X[c] = X[c].fillna(X[c].mode()[0])
|
|
65
|
+
return pd.get_dummies(X, drop_first=True, dtype=int), keep
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def ask_columns(df, target, dropped):
|
|
69
|
+
print(f"\nAccuracy is below {MIN_ACCURACY:.0%}. You can choose the columns yourself.")
|
|
70
|
+
print("Columns:", list(df.columns))
|
|
71
|
+
new_target = input(f"Target column (Enter to keep '{target}'): ").strip() or target
|
|
72
|
+
if new_target not in df.columns:
|
|
73
|
+
print(f"'{new_target}' not found, keeping '{target}'.")
|
|
74
|
+
new_target = target
|
|
75
|
+
drop = [c.strip() for c in input("Columns to leave out, comma separated (Enter for none): ").split(",")]
|
|
76
|
+
drop = [c for c in drop if c in df.columns and c != new_target]
|
|
77
|
+
changed = new_target != target or set(drop) != set(dropped)
|
|
78
|
+
return new_target, drop, changed
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# ---------- 1. Load dataset ----------
|
|
82
|
+
path = input("Enter CSV file path or press Enter for built-in Breast Cancer data: ").strip().strip('"')
|
|
83
|
+
if path:
|
|
84
|
+
df = read_csv(path)
|
|
85
|
+
else:
|
|
86
|
+
data = load_breast_cancer(as_frame=True)
|
|
87
|
+
df = data.frame.copy()
|
|
88
|
+
df["target"] = data.target_names[data.target]
|
|
89
|
+
print("Using built-in Breast Cancer dataset")
|
|
90
|
+
print(df.head())
|
|
91
|
+
print("Shape:", df.shape)
|
|
92
|
+
|
|
93
|
+
target = guess_target(df)
|
|
94
|
+
dropped = []
|
|
95
|
+
|
|
96
|
+
# ---------- 2. Train (repeats if accuracy is low and you choose other columns) ----------
|
|
97
|
+
while True:
|
|
98
|
+
data = df.dropna(subset=[target]).drop_duplicates()
|
|
99
|
+
y, class_names = make_labels(data[target])
|
|
100
|
+
X, used = make_features(data.drop(columns=[target] + dropped))
|
|
101
|
+
print(f"\nTarget column: {target} -> classes {class_names}")
|
|
102
|
+
print("Feature columns used:", used)
|
|
103
|
+
|
|
104
|
+
stratify = y if np.bincount(y).min() >= 2 else None
|
|
105
|
+
X_train, X_test, y_train, y_test = train_test_split(
|
|
106
|
+
X, y, test_size=0.2, random_state=42, stratify=stratify
|
|
107
|
+
)
|
|
108
|
+
scaler = StandardScaler()
|
|
109
|
+
X_train = scaler.fit_transform(X_train)
|
|
110
|
+
X_test = scaler.transform(X_test)
|
|
111
|
+
|
|
112
|
+
# Deep feed forward network
|
|
113
|
+
model = Sequential([
|
|
114
|
+
Input(shape=(X_train.shape[1],)),
|
|
115
|
+
Dense(128, activation="relu"),
|
|
116
|
+
Dropout(0.3),
|
|
117
|
+
Dense(64, activation="relu"),
|
|
118
|
+
Dropout(0.2),
|
|
119
|
+
Dense(32, activation="relu"),
|
|
120
|
+
Dense(len(class_names), activation="softmax"),
|
|
121
|
+
])
|
|
122
|
+
model.compile(optimizer="adam", loss="sparse_categorical_crossentropy", metrics=["accuracy"])
|
|
123
|
+
model.fit(X_train, y_train, epochs=50, batch_size=32, validation_split=0.2, verbose=0,
|
|
124
|
+
callbacks=[EarlyStopping(monitor="val_loss", patience=5, restore_best_weights=True)])
|
|
125
|
+
|
|
126
|
+
test_acc = model.evaluate(X_test, y_test, verbose=0)[1]
|
|
127
|
+
print(f"Testing Accuracy: {test_acc * 100:.2f}%")
|
|
128
|
+
if test_acc >= MIN_ACCURACY:
|
|
129
|
+
break
|
|
130
|
+
target, dropped, changed = ask_columns(df, target, dropped)
|
|
131
|
+
if not changed:
|
|
132
|
+
break
|
|
133
|
+
|
|
134
|
+
# ---------- 3. Final results ----------
|
|
135
|
+
model.summary()
|
|
136
|
+
train_acc = model.evaluate(X_train, y_train, verbose=0)[1]
|
|
137
|
+
y_pred = model.predict(X_test, verbose=0).argmax(axis=1)
|
|
138
|
+
|
|
139
|
+
print(f"\nTraining Accuracy: {train_acc * 100:.2f}%")
|
|
140
|
+
print(f"Testing Accuracy: {test_acc * 100:.2f}%")
|
|
141
|
+
print("\nClassification Report:")
|
|
142
|
+
print(classification_report(y_test, y_pred, labels=range(len(class_names)),
|
|
143
|
+
target_names=class_names, zero_division=0))
|
|
144
|
+
print("Confusion Matrix:")
|
|
145
|
+
print(confusion_matrix(y_test, y_pred))
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# Regularization and Optimization - works with any CSV classification dataset
|
|
2
|
+
# Compares: no regularization vs L2 + Dropout + Early Stopping,
|
|
3
|
+
# and the optimizers Adam, SGD and RMSprop.
|
|
4
|
+
# The target column and the feature columns are picked automatically.
|
|
5
|
+
# If accuracy is low, you are asked which columns to use and the models are retrained.
|
|
6
|
+
# Press Enter to use the built-in Wine dataset (from scikit-learn, no download).
|
|
7
|
+
import numpy as np
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import matplotlib.pyplot as plt
|
|
10
|
+
from sklearn.datasets import load_wine
|
|
11
|
+
from sklearn.model_selection import train_test_split
|
|
12
|
+
from sklearn.preprocessing import StandardScaler, LabelEncoder
|
|
13
|
+
from sklearn.metrics import classification_report, confusion_matrix
|
|
14
|
+
from tensorflow.keras.models import Sequential
|
|
15
|
+
from tensorflow.keras.layers import Input, Dense, Dropout
|
|
16
|
+
from tensorflow.keras.regularizers import l2
|
|
17
|
+
from tensorflow.keras.callbacks import EarlyStopping
|
|
18
|
+
|
|
19
|
+
MIN_ACCURACY = 0.70 # below this you get to choose the columns yourself
|
|
20
|
+
TARGET_NAMES = {"target", "label", "labels", "class", "output", "outcome", "result", "y"}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# ---------- helpers ----------
|
|
24
|
+
def read_csv(path):
|
|
25
|
+
"""Read a CSV with any separator (, ; tab |) and encoding."""
|
|
26
|
+
with open(path, encoding="latin-1") as f:
|
|
27
|
+
first_line = f.readline()
|
|
28
|
+
sep = max([",", ";", "\t", "|"], key=first_line.count)
|
|
29
|
+
try:
|
|
30
|
+
df = pd.read_csv(path, sep=sep)
|
|
31
|
+
except UnicodeDecodeError:
|
|
32
|
+
df = pd.read_csv(path, sep=sep, encoding="latin-1")
|
|
33
|
+
df = df.loc[:, ~df.columns.astype(str).str.startswith("Unnamed")]
|
|
34
|
+
return df.dropna(axis=1, how="all")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def is_id(col):
|
|
38
|
+
name = str(col.name).lower()
|
|
39
|
+
return (name in ("id", "index") or name.endswith("_id")
|
|
40
|
+
or (pd.api.types.is_integer_dtype(col) and col.nunique() == len(col)))
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def guess_target(df):
|
|
44
|
+
for c in df.columns:
|
|
45
|
+
if str(c).lower() in TARGET_NAMES:
|
|
46
|
+
return c
|
|
47
|
+
return [c for c in df.columns if not is_id(df[c]) and df[c].nunique() >= 2][-1]
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def make_labels(col):
|
|
51
|
+
"""Numeric columns with many values become High / Low (split at the median)."""
|
|
52
|
+
if pd.api.types.is_numeric_dtype(col) and col.nunique() > 10:
|
|
53
|
+
col = pd.Series(np.where(col >= col.median(), "High", "Low"), index=col.index)
|
|
54
|
+
encoder = LabelEncoder()
|
|
55
|
+
return encoder.fit_transform(col.astype(str)), [str(c) for c in encoder.classes_]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def make_features(X):
|
|
59
|
+
"""Drop ID / constant / free-text columns, fill missing values, one-hot encode."""
|
|
60
|
+
keep = [c for c in X.columns
|
|
61
|
+
if not is_id(X[c]) and X[c].nunique() > 1
|
|
62
|
+
and (pd.api.types.is_numeric_dtype(X[c]) or X[c].nunique() <= 50)]
|
|
63
|
+
X = X[keep].copy()
|
|
64
|
+
for c in X.columns:
|
|
65
|
+
if pd.api.types.is_numeric_dtype(X[c]):
|
|
66
|
+
X[c] = X[c].fillna(X[c].median())
|
|
67
|
+
else:
|
|
68
|
+
X[c] = X[c].fillna(X[c].mode()[0])
|
|
69
|
+
return pd.get_dummies(X, drop_first=True, dtype=int), keep
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def ask_columns(df, target, dropped):
|
|
73
|
+
print(f"\nBest accuracy is below {MIN_ACCURACY:.0%}. You can choose the columns yourself.")
|
|
74
|
+
print("Columns:", list(df.columns))
|
|
75
|
+
new_target = input(f"Target column (Enter to keep '{target}'): ").strip() or target
|
|
76
|
+
if new_target not in df.columns:
|
|
77
|
+
print(f"'{new_target}' not found, keeping '{target}'.")
|
|
78
|
+
new_target = target
|
|
79
|
+
drop = [c.strip() for c in input("Columns to leave out, comma separated (Enter for none): ").split(",")]
|
|
80
|
+
drop = [c for c in drop if c in df.columns and c != new_target]
|
|
81
|
+
changed = new_target != target or set(drop) != set(dropped)
|
|
82
|
+
return new_target, drop, changed
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def build_model(n_inputs, n_classes, regularize, optimizer):
|
|
86
|
+
reg = l2(0.001) if regularize else None
|
|
87
|
+
drop = 0.3 if regularize else 0.0
|
|
88
|
+
model = Sequential([
|
|
89
|
+
Input(shape=(n_inputs,)),
|
|
90
|
+
Dense(128, activation="relu", kernel_regularizer=reg),
|
|
91
|
+
Dropout(drop),
|
|
92
|
+
Dense(64, activation="relu", kernel_regularizer=reg),
|
|
93
|
+
Dropout(drop),
|
|
94
|
+
Dense(n_classes, activation="softmax"),
|
|
95
|
+
])
|
|
96
|
+
model.compile(optimizer=optimizer, loss="sparse_categorical_crossentropy", metrics=["accuracy"])
|
|
97
|
+
return model
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
EXPERIMENTS = [
|
|
101
|
+
("No regularization (Adam)", False, "adam"),
|
|
102
|
+
("L2 + Dropout + EarlyStop (Adam)", True, "adam"),
|
|
103
|
+
("L2 + Dropout + EarlyStop (SGD)", True, "sgd"),
|
|
104
|
+
("L2 + Dropout + EarlyStop (RMSprop)", True, "rmsprop"),
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
# ---------- 1. Load dataset ----------
|
|
108
|
+
path = input("Enter CSV file path or press Enter for built-in Wine data: ").strip().strip('"')
|
|
109
|
+
if path:
|
|
110
|
+
df = read_csv(path)
|
|
111
|
+
else:
|
|
112
|
+
data = load_wine(as_frame=True)
|
|
113
|
+
df = data.frame.copy()
|
|
114
|
+
df["target"] = data.target_names[data.target]
|
|
115
|
+
print("Using built-in Wine dataset")
|
|
116
|
+
print(df.head())
|
|
117
|
+
print("Shape:", df.shape)
|
|
118
|
+
|
|
119
|
+
target = guess_target(df)
|
|
120
|
+
dropped = []
|
|
121
|
+
|
|
122
|
+
# ---------- 2. Train all experiments (repeats if accuracy is low and you choose other columns) ----------
|
|
123
|
+
while True:
|
|
124
|
+
data = df.dropna(subset=[target]).drop_duplicates()
|
|
125
|
+
y, class_names = make_labels(data[target])
|
|
126
|
+
X, used = make_features(data.drop(columns=[target] + dropped))
|
|
127
|
+
print(f"\nTarget column: {target} -> classes {class_names}")
|
|
128
|
+
print("Feature columns used:", used)
|
|
129
|
+
|
|
130
|
+
stratify = y if np.bincount(y).min() >= 2 else None
|
|
131
|
+
X_train, X_test, y_train, y_test = train_test_split(
|
|
132
|
+
X, y, test_size=0.2, random_state=42, stratify=stratify
|
|
133
|
+
)
|
|
134
|
+
scaler = StandardScaler()
|
|
135
|
+
X_train = scaler.fit_transform(X_train)
|
|
136
|
+
X_test = scaler.transform(X_test)
|
|
137
|
+
|
|
138
|
+
results, histories, models = [], {}, {}
|
|
139
|
+
for name, regularize, optimizer in EXPERIMENTS:
|
|
140
|
+
print("Training:", name)
|
|
141
|
+
model = build_model(X_train.shape[1], len(class_names), regularize, optimizer)
|
|
142
|
+
callbacks = [EarlyStopping(monitor="val_loss", patience=5, restore_best_weights=True)] if regularize else []
|
|
143
|
+
history = model.fit(X_train, y_train, epochs=50, batch_size=32,
|
|
144
|
+
validation_split=0.2, callbacks=callbacks, verbose=0)
|
|
145
|
+
train_acc = model.evaluate(X_train, y_train, verbose=0)[1]
|
|
146
|
+
test_acc = model.evaluate(X_test, y_test, verbose=0)[1]
|
|
147
|
+
results.append([name, train_acc * 100, test_acc * 100, len(history.history["loss"])])
|
|
148
|
+
histories[name] = history
|
|
149
|
+
models[name] = model
|
|
150
|
+
|
|
151
|
+
results_df = pd.DataFrame(results, columns=["Model", "Train Acc", "Test Acc", "Epochs"])
|
|
152
|
+
best_acc = results_df["Test Acc"].max() / 100
|
|
153
|
+
print(f"Best Test Accuracy: {best_acc * 100:.2f}%")
|
|
154
|
+
if best_acc >= MIN_ACCURACY:
|
|
155
|
+
break
|
|
156
|
+
target, dropped, changed = ask_columns(df, target, dropped)
|
|
157
|
+
if not changed:
|
|
158
|
+
break
|
|
159
|
+
|
|
160
|
+
# ---------- 3. Compare results ----------
|
|
161
|
+
print("\nComparison:")
|
|
162
|
+
print(results_df.round(2).to_string(index=False))
|
|
163
|
+
|
|
164
|
+
plt.figure(figsize=(9, 5))
|
|
165
|
+
for name, history in histories.items():
|
|
166
|
+
plt.plot(history.history["val_loss"], label=name)
|
|
167
|
+
plt.title("Validation Loss: Regularization and Optimizers")
|
|
168
|
+
plt.xlabel("Epoch")
|
|
169
|
+
plt.ylabel("Validation Loss")
|
|
170
|
+
plt.legend()
|
|
171
|
+
plt.grid(True)
|
|
172
|
+
plt.show()
|
|
173
|
+
|
|
174
|
+
best = results_df.sort_values("Test Acc", ascending=False).iloc[0]["Model"]
|
|
175
|
+
y_pred = models[best].predict(X_test, verbose=0).argmax(axis=1)
|
|
176
|
+
print(f"\nBest model: {best}")
|
|
177
|
+
print(classification_report(y_test, y_pred, labels=range(len(class_names)),
|
|
178
|
+
target_names=class_names, zero_division=0))
|
|
179
|
+
print("Confusion Matrix:")
|
|
180
|
+
print(confusion_matrix(y_test, y_pred))
|