Shoker2 commited on
Commit
242c54b
·
0 Parent(s):
Files changed (3) hide show
  1. .gitignore +21 -0
  2. evaluate.py +38 -0
  3. mnist.py +178 -0
.gitignore ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # Other
13
+ .env
14
+ cmds.txt
15
+ uv.lock
16
+ .vscode
17
+ test.drawio
18
+ test.py
19
+ models/*
20
+ Fashion-MNIST/
21
+ MNIST/
evaluate.py ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+
3
+ import pandas as pd
4
+ from sklearn.metrics import accuracy_score, confusion_matrix
5
+
6
+
7
+ def main():
8
+ parser = argparse.ArgumentParser()
9
+ parser.add_argument("--ground-truth", required=True)
10
+ parser.add_argument("--predictions", required=True)
11
+
12
+ args = parser.parse_args()
13
+
14
+ df_true = pd.read_csv(args.ground_truth)
15
+ df_pred = pd.read_csv(args.predictions)
16
+
17
+ if "label" not in df_true.columns or "label" not in df_pred.columns:
18
+ raise ValueError("Оба файла должны содержать колонку 'label'")
19
+
20
+ if len(df_true) != len(df_pred):
21
+ raise ValueError(
22
+ f"Разная длина файлов: ground-truth={len(df_true)}, "
23
+ f"predictions={len(df_pred)}"
24
+ )
25
+
26
+ y_true = df_true["label"].values
27
+ y_pred = df_pred["label"].values
28
+
29
+ acc = accuracy_score(y_true, y_pred)
30
+ cm = confusion_matrix(y_true, y_pred)
31
+
32
+ print(f"Accuracy: {acc:.4f}")
33
+ print("Confusion matrix:")
34
+ print(cm)
35
+
36
+
37
+ if __name__ == "__main__":
38
+ main()
mnist.py ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import argparse
2
+
3
+ import numpy as np
4
+ import pandas as pd
5
+
6
+ import torch
7
+ import torch.nn as nn
8
+ import torch.optim as optim
9
+ from torch.utils.data import DataLoader, TensorDataset
10
+
11
+
12
+ class ModelCNN(nn.Module):
13
+ """
14
+ Архитектура:
15
+ INPUT (1x28x28) ->
16
+ [CONV -> RELU -> CONV -> RELU -> POOL] * 3 ->
17
+ [FC -> RELU] * 2 ->
18
+ FC (num_classes)
19
+ """
20
+
21
+ def __init__(self, num_classes=10):
22
+ super(ModelCNN, self).__init__()
23
+
24
+ self.features = nn.Sequential(
25
+ # блок 1
26
+ nn.Conv2d(1, 32, kernel_size=3, padding=1),
27
+ nn.ReLU(inplace=True),
28
+ nn.Conv2d(32, 32, kernel_size=3, padding=1),
29
+ nn.ReLU(inplace=True),
30
+ nn.MaxPool2d(2), # 28 -> 14
31
+ # блок 2
32
+ nn.Conv2d(32, 64, kernel_size=3, padding=1),
33
+ nn.ReLU(inplace=True),
34
+ nn.Conv2d(64, 64, kernel_size=3, padding=1),
35
+ nn.ReLU(inplace=True),
36
+ nn.MaxPool2d(2), # 14 -> 7
37
+ # блок 3
38
+ nn.Conv2d(64, 128, kernel_size=3, padding=1),
39
+ nn.ReLU(inplace=True),
40
+ nn.Conv2d(128, 128, kernel_size=3, padding=1),
41
+ nn.ReLU(inplace=True),
42
+ nn.MaxPool2d(2), # 7 -> 3
43
+ )
44
+
45
+ self.classifier = nn.Sequential(
46
+ nn.Linear(128 * 3 * 3, 256),
47
+ nn.ReLU(inplace=True),
48
+ nn.Linear(256, 128),
49
+ nn.ReLU(inplace=True),
50
+ nn.Linear(128, num_classes),
51
+ )
52
+
53
+ def forward(self, x):
54
+ x = self.features(x)
55
+ x = x.view(x.size(0), -1) # (N, 128*3*3)
56
+ x = self.classifier(x)
57
+ return x
58
+
59
+
60
+ def train_mode(args):
61
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
62
+ print(f"Устройство: {device}")
63
+
64
+ df = pd.read_csv(args.input)
65
+
66
+ labels = df["label"].astype(np.int64).values
67
+ pixels = df.drop(columns=["label"]).values.astype(np.float32) / 255.0
68
+
69
+ images = torch.from_numpy(pixels.reshape(-1, 1, 28, 28))
70
+ labels = torch.from_numpy(labels)
71
+
72
+ dataset = TensorDataset(images, labels)
73
+ dataloader = DataLoader(dataset, batch_size=args.batch_size, shuffle=True)
74
+
75
+ model = ModelCNN(num_classes=args.num_classes).to(device)
76
+ criterion = nn.CrossEntropyLoss()
77
+ optimizer = optim.Adam(model.parameters(), lr=args.lr)
78
+
79
+ model.train()
80
+ for epoch in range(args.epochs):
81
+ for i, (images, labels) in enumerate(dataloader):
82
+ images = images.to(device)
83
+ labels = labels.to(device)
84
+
85
+ outputs = model(images)
86
+ loss = criterion(outputs, labels)
87
+
88
+ optimizer.zero_grad()
89
+ loss.backward()
90
+ optimizer.step()
91
+
92
+ if (i + 1) % 100 == 0:
93
+ print(
94
+ f"Epoch [{epoch+1}/{args.epochs}], Step [{i+1}/{len(dataloader)}], Loss: {loss.item():.4f}"
95
+ )
96
+
97
+ checkpoint = {
98
+ "state_dict": model.state_dict(),
99
+ "num_classes": args.num_classes,
100
+ }
101
+ torch.save(checkpoint, args.model)
102
+
103
+
104
+ def inference_mode(args):
105
+ device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
106
+ print(f"Устройство: {device}")
107
+
108
+ checkpoint = torch.load(args.model, map_location=device)
109
+ num_classes = checkpoint.get("num_classes", 10)
110
+
111
+ model = ModelCNN(num_classes=num_classes).to(device)
112
+ model.load_state_dict(checkpoint["state_dict"])
113
+ model.eval()
114
+
115
+ df_test = pd.read_csv(args.input)
116
+
117
+ has_label = "label" in df_test.columns
118
+ if has_label:
119
+ pixels = df_test.drop(columns=["label"]).values
120
+ else:
121
+ pixels = df_test.values
122
+
123
+ pixels = pixels.astype(np.float32) / 255.0
124
+ images = torch.from_numpy(pixels.reshape(-1, 1, 28, 28))
125
+
126
+ dataset = TensorDataset(images)
127
+ dataloader = DataLoader(dataset, batch_size=args.batch_size, shuffle=False)
128
+
129
+ all_preds = []
130
+
131
+ with torch.no_grad():
132
+ for (batch_images,) in dataloader:
133
+ batch_images = batch_images.to(device)
134
+ outputs = model(batch_images)
135
+ _, preds = torch.max(outputs, 1)
136
+ all_preds.extend(preds.cpu().numpy().tolist())
137
+
138
+ df_pred = df_test.copy()
139
+ df_pred["label"] = all_preds
140
+
141
+ df_pred.to_csv(args.output, index=False)
142
+
143
+
144
+ def parse_args():
145
+ parser = argparse.ArgumentParser()
146
+
147
+ parser.add_argument("--mode", choices=["train", "inference"], required=True)
148
+ parser.add_argument("--input", type=str)
149
+ parser.add_argument("--output", type=str)
150
+ parser.add_argument("--model", type=str, required=True)
151
+
152
+ parser.add_argument("--epochs", type=int, default=5)
153
+ parser.add_argument("--batch-size", type=int, default=64)
154
+ parser.add_argument("--lr", type=float, default=0.001)
155
+ parser.add_argument("--num-classes", type=int, default=10)
156
+
157
+ args = parser.parse_args()
158
+
159
+ if args.mode == "train":
160
+ if args.input is None:
161
+ parser.error("--input обязателен в режиме train")
162
+ elif args.mode == "inference":
163
+ if args.input is None or args.output is None:
164
+ parser.error("--input и --output обязательны в режиме inference")
165
+
166
+ return args
167
+
168
+
169
+ def main():
170
+ args = parse_args()
171
+ if args.mode == "train":
172
+ train_mode(args)
173
+ else:
174
+ inference_mode(args)
175
+
176
+
177
+ if __name__ == "__main__":
178
+ main()