Skip to content

Commit c80a2cb

Browse files
Merge pull request #2 from michabirklbauer/develop
add wf and tests
2 parents 91809d3 + e11a1bb commit c80a2cb

File tree

4 files changed

+144
-4
lines changed

4 files changed

+144
-4
lines changed

.github/workflows/python-app.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
2+
# Reference workflow provided by (c) GitHub
3+
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
4+
5+
name: neuralnet
6+
7+
on:
8+
push:
9+
branches: [ master ]
10+
pull_request:
11+
branches: [ master ]
12+
13+
jobs:
14+
build:
15+
16+
runs-on: ubuntu-latest
17+
strategy:
18+
matrix:
19+
python-version: ['3.7', '3.8', '3.9', '3.10']
20+
21+
steps:
22+
- uses: actions/checkout@v2
23+
- name: Set up Python ${{ matrix.python-version }}
24+
uses: actions/setup-python@v2
25+
with:
26+
python-version: ${{ matrix.python-version }}
27+
- name: Install dependencies
28+
run: |
29+
python -m pip install --upgrade pip
30+
python -m pip install flake8 pytest
31+
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
32+
- name: Lint with flake8
33+
run: |
34+
# stop the build if there are Python syntax errors or undefined names
35+
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
36+
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
37+
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
38+
- name: Test with pytest
39+
run: |
40+
cp neuralnet.py tests/neuralnet.py
41+
pytest tests/tests.py

neuralnet-colab.ipynb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -402,7 +402,7 @@
402402
" elif self.layers[i][\"activation\"] == \"softmax\":\n",
403403
" self.layers[i][\"A\"] = ActivationFunctions.softmax(self.layers[i][\"L\"])\n",
404404
" else:\n",
405-
" raise NotImplementedError(\"Activation function '\" + layer[\"activation\"] + \"' not implemented!\")\n",
405+
" raise NotImplementedError(\"Activation function '\" + self.layers[i][\"activation\"] + \"' not implemented!\")\n",
406406
"\n",
407407
" # back propagation\n",
408408
" def __back_propagation(self, data: np.array, target: np.array, learning_rate: float = 0.1) -> float:\n",
@@ -494,7 +494,7 @@
494494
" self.layers[-1][\"W\"] -= learning_rate * dW\n",
495495
" self.layers[-1][\"b\"] -= learning_rate * db\n",
496496
" else:\n",
497-
" raise NotImplementedError(\"The combination of '\" + self.loss + \" loss' and '\" + self.layers[i][\"activation\"] + \" activation' is not implemented!\")\n",
497+
" raise NotImplementedError(\"The combination of '\" + self.loss + \" loss' and '\" + self.layers[-1][\"activation\"] + \" activation' is not implemented!\")\n",
498498
"\n",
499499
" # back propagation through the remaining hidden layers\n",
500500
" for i in reversed(range(len(self.layers) - 1)):\n",

neuralnet.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -358,7 +358,7 @@ def __forward_propagation(self, data: np.array) -> None:
358358
elif self.layers[i]["activation"] == "softmax":
359359
self.layers[i]["A"] = ActivationFunctions.softmax(self.layers[i]["L"])
360360
else:
361-
raise NotImplementedError("Activation function '" + layer["activation"] + "' not implemented!")
361+
raise NotImplementedError("Activation function '" + self.layers[i]["activation"] + "' not implemented!")
362362

363363
# back propagation
364364
def __back_propagation(self, data: np.array, target: np.array, learning_rate: float = 0.1) -> float:
@@ -450,7 +450,7 @@ class labels of the input data
450450
self.layers[-1]["W"] -= learning_rate * dW
451451
self.layers[-1]["b"] -= learning_rate * db
452452
else:
453-
raise NotImplementedError("The combination of '" + self.loss + " loss' and '" + self.layers[i]["activation"] + " activation' is not implemented!")
453+
raise NotImplementedError("The combination of '" + self.loss + " loss' and '" + self.layers[-1]["activation"] + " activation' is not implemented!")
454454

455455
# back propagation through the remaining hidden layers
456456
for i in reversed(range(len(self.layers) - 1)):

tests/tests.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#!/usr/bin/env python3
2+
3+
# NEURAL NETWORK IMPLEMENTATION - TESTS
4+
# 2022 (c) Micha Johannes Birklbauer
5+
# https://github.com/michabirklbauer/
6+
7+
8+
def test_bcc():
9+
10+
#### Binary-class Classification ####
11+
12+
from zipfile import ZipFile as zip
13+
14+
with zip("data.zip") as f:
15+
f.extractall()
16+
f.close()
17+
18+
from neuralnet import NeuralNetwork
19+
import numpy as np
20+
import pandas as pd
21+
from sklearn.metrics import accuracy_score
22+
from sklearn.preprocessing import OneHotEncoder
23+
from sklearn.model_selection import train_test_split
24+
25+
data = pd.read_csv("binaryclass_train.csv", header = None)
26+
data["label"] = data[1].apply(lambda x: 1 if x == "M" else 0)
27+
train, test = train_test_split(data, test_size = 0.3)
28+
train_data = train.loc[:, ~train.columns.isin([0, 1, "label"])].to_numpy()
29+
train_target = train["label"].to_numpy()
30+
test_data = test.loc[:, ~test.columns.isin([0, 1, "label"])].to_numpy()
31+
test_target = test["label"].to_numpy()
32+
33+
NN = NeuralNetwork(input_size = train_data.shape[1])
34+
NN.add_layer(16, "relu")
35+
NN.add_layer(16, "relu")
36+
NN.add_layer(1, "sigmoid")
37+
NN.compile(loss = "binary crossentropy")
38+
NN.summary()
39+
40+
hist = NN.fit(train_data, train_target, epochs = 1000, batch_size = 32, learning_rate = 0.01)
41+
42+
train_predictions = np.round(NN.predict(train_data))
43+
train_acc = accuracy_score(train["label"].to_numpy(), train_predictions)
44+
test_predictions = np.round(NN.predict(test_data))
45+
test_acc = accuracy_score(test["label"].to_numpy(), test_predictions)
46+
47+
import os
48+
os.remove("binaryclass_train.csv")
49+
os.remove("multiclass_train.csv")
50+
51+
assert train_acc > 0.85 and test_acc > 0.85
52+
53+
def test_mcc():
54+
55+
#### Multi-class Classification ####
56+
57+
from zipfile import ZipFile as zip
58+
59+
with zip("data.zip") as f:
60+
f.extractall()
61+
f.close()
62+
63+
from neuralnet import NeuralNetwork
64+
import numpy as np
65+
import pandas as pd
66+
from sklearn.metrics import accuracy_score
67+
from sklearn.preprocessing import OneHotEncoder
68+
from sklearn.model_selection import train_test_split
69+
70+
data = pd.read_csv("multiclass_train.csv")
71+
train, test = train_test_split(data, test_size = 0.3)
72+
train_data = train.loc[:, train.columns != "label"].to_numpy() / 255
73+
train_target = train["label"].to_numpy()
74+
test_data = test.loc[:, test.columns != "label"].to_numpy() / 255
75+
test_target = test["label"].to_numpy()
76+
77+
one_hot = OneHotEncoder(sparse = False, categories = "auto")
78+
train_target = one_hot.fit_transform(train_target.reshape(-1, 1))
79+
test_target = one_hot.transform(test_target.reshape(-1, 1))
80+
81+
NN = NeuralNetwork(input_size = train_data.shape[1])
82+
NN.add_layer(32, "relu")
83+
NN.add_layer(16, "relu")
84+
NN.add_layer(10, "softmax")
85+
NN.compile(loss = "categorical crossentropy")
86+
NN.summary()
87+
88+
hist = NN.fit(train_data, train_target, epochs = 30, batch_size = 16, learning_rate = 0.05)
89+
90+
train_predictions = np.argmax(NN.predict(train_data), axis = 1)
91+
train_acc = accuracy_score(train["label"].to_numpy(), train_predictions)
92+
test_predictions = np.argmax(NN.predict(test_data), axis = 1)
93+
test_acc = accuracy_score(test["label"].to_numpy(), test_predictions)
94+
95+
import os
96+
os.remove("binaryclass_train.csv")
97+
os.remove("multiclass_train.csv")
98+
99+
assert train_acc > 0.85 and test_acc > 0.85

0 commit comments

Comments
 (0)