Skip to content

Commit

Permalink
Merge pull request #2 from michabirklbauer/develop
Browse files Browse the repository at this point in the history
add wf and tests
  • Loading branch information
michabirklbauer authored Dec 30, 2022
2 parents 91809d3 + e11a1bb commit c80a2cb
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 4 deletions.
41 changes: 41 additions & 0 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# Reference workflow provided by (c) GitHub
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions

name: neuralnet

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
build:

runs-on: ubuntu-latest
strategy:
matrix:
python-version: ['3.7', '3.8', '3.9', '3.10']

steps:
- uses: actions/checkout@v2
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Lint with flake8
run: |
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
cp neuralnet.py tests/neuralnet.py
pytest tests/tests.py
4 changes: 2 additions & 2 deletions neuralnet-colab.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@
" elif self.layers[i][\"activation\"] == \"softmax\":\n",
" self.layers[i][\"A\"] = ActivationFunctions.softmax(self.layers[i][\"L\"])\n",
" else:\n",
" raise NotImplementedError(\"Activation function '\" + layer[\"activation\"] + \"' not implemented!\")\n",
" raise NotImplementedError(\"Activation function '\" + self.layers[i][\"activation\"] + \"' not implemented!\")\n",
"\n",
" # back propagation\n",
" def __back_propagation(self, data: np.array, target: np.array, learning_rate: float = 0.1) -> float:\n",
Expand Down Expand Up @@ -494,7 +494,7 @@
" self.layers[-1][\"W\"] -= learning_rate * dW\n",
" self.layers[-1][\"b\"] -= learning_rate * db\n",
" else:\n",
" raise NotImplementedError(\"The combination of '\" + self.loss + \" loss' and '\" + self.layers[i][\"activation\"] + \" activation' is not implemented!\")\n",
" raise NotImplementedError(\"The combination of '\" + self.loss + \" loss' and '\" + self.layers[-1][\"activation\"] + \" activation' is not implemented!\")\n",
"\n",
" # back propagation through the remaining hidden layers\n",
" for i in reversed(range(len(self.layers) - 1)):\n",
Expand Down
4 changes: 2 additions & 2 deletions neuralnet.py
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ def __forward_propagation(self, data: np.array) -> None:
elif self.layers[i]["activation"] == "softmax":
self.layers[i]["A"] = ActivationFunctions.softmax(self.layers[i]["L"])
else:
raise NotImplementedError("Activation function '" + layer["activation"] + "' not implemented!")
raise NotImplementedError("Activation function '" + self.layers[i]["activation"] + "' not implemented!")

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

# back propagation through the remaining hidden layers
for i in reversed(range(len(self.layers) - 1)):
Expand Down
99 changes: 99 additions & 0 deletions tests/tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/usr/bin/env python3

# NEURAL NETWORK IMPLEMENTATION - TESTS
# 2022 (c) Micha Johannes Birklbauer
# https://github.com/michabirklbauer/
# micha.birklbauer@gmail.com

def test_bcc():

#### Binary-class Classification ####

from zipfile import ZipFile as zip

with zip("data.zip") as f:
f.extractall()
f.close()

from neuralnet import NeuralNetwork
import numpy as np
import pandas as pd
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import OneHotEncoder
from sklearn.model_selection import train_test_split

data = pd.read_csv("binaryclass_train.csv", header = None)
data["label"] = data[1].apply(lambda x: 1 if x == "M" else 0)
train, test = train_test_split(data, test_size = 0.3)
train_data = train.loc[:, ~train.columns.isin([0, 1, "label"])].to_numpy()
train_target = train["label"].to_numpy()
test_data = test.loc[:, ~test.columns.isin([0, 1, "label"])].to_numpy()
test_target = test["label"].to_numpy()

NN = NeuralNetwork(input_size = train_data.shape[1])
NN.add_layer(16, "relu")
NN.add_layer(16, "relu")
NN.add_layer(1, "sigmoid")
NN.compile(loss = "binary crossentropy")
NN.summary()

hist = NN.fit(train_data, train_target, epochs = 1000, batch_size = 32, learning_rate = 0.01)

train_predictions = np.round(NN.predict(train_data))
train_acc = accuracy_score(train["label"].to_numpy(), train_predictions)
test_predictions = np.round(NN.predict(test_data))
test_acc = accuracy_score(test["label"].to_numpy(), test_predictions)

import os
os.remove("binaryclass_train.csv")
os.remove("multiclass_train.csv")

assert train_acc > 0.85 and test_acc > 0.85

def test_mcc():

#### Multi-class Classification ####

from zipfile import ZipFile as zip

with zip("data.zip") as f:
f.extractall()
f.close()

from neuralnet import NeuralNetwork
import numpy as np
import pandas as pd
from sklearn.metrics import accuracy_score
from sklearn.preprocessing import OneHotEncoder
from sklearn.model_selection import train_test_split

data = pd.read_csv("multiclass_train.csv")
train, test = train_test_split(data, test_size = 0.3)
train_data = train.loc[:, train.columns != "label"].to_numpy() / 255
train_target = train["label"].to_numpy()
test_data = test.loc[:, test.columns != "label"].to_numpy() / 255
test_target = test["label"].to_numpy()

one_hot = OneHotEncoder(sparse = False, categories = "auto")
train_target = one_hot.fit_transform(train_target.reshape(-1, 1))
test_target = one_hot.transform(test_target.reshape(-1, 1))

NN = NeuralNetwork(input_size = train_data.shape[1])
NN.add_layer(32, "relu")
NN.add_layer(16, "relu")
NN.add_layer(10, "softmax")
NN.compile(loss = "categorical crossentropy")
NN.summary()

hist = NN.fit(train_data, train_target, epochs = 30, batch_size = 16, learning_rate = 0.05)

train_predictions = np.argmax(NN.predict(train_data), axis = 1)
train_acc = accuracy_score(train["label"].to_numpy(), train_predictions)
test_predictions = np.argmax(NN.predict(test_data), axis = 1)
test_acc = accuracy_score(test["label"].to_numpy(), test_predictions)

import os
os.remove("binaryclass_train.csv")
os.remove("multiclass_train.csv")

assert train_acc > 0.85 and test_acc > 0.85

0 comments on commit c80a2cb

Please sign in to comment.