CIFAR-10 Training // Konrad Krawczyk

The task was to train a classification model based on a CIFAR dataset. As the official website mentions, the dataset consists of 60000 32×32 colour images in 10 classes: airplane, automobile, bird, cat, deer, dog, frog, horse, ship and truck. Object recognition is a task that largely depends on the level of semantic distance between concepts. It’s been quite easy to make distinctions between different types of clothing (as in the Fashion MNIST dataset) than it is to tell the difference between a cat and a dog, especially given that the images present objects in different lighting and color context. Therefore, at no point did I expect to achieve very high accuracy. However, I wanted to test out the dataset with various parameters, mostly changing the batch size and the number of epochs.

I started off with 100 epochs, as it is given in the example code. However, this process would be painstakingly long and the potential improvement would be marginal, so I decided to go for 3 epochs instead. Then, I tested the algorithm with 8 batch sizes. The results shown below are respectively for batch size 16, 23, 64, 128, 236, 512, 1024, and 2048:

       

The highest accuracy has occurred for batch size 16, (3 epochs) in which the returned accuracy value was above 0.6. It is not an incredibly high accuracy, however it accomplishes some level of accurate classification above chance. One limitation is the speed of my computer (I trained the examples on my MacBook Air) which took a significant amount of time.

BIRS Midterm Documentation // Konrad Krawczyk

 

1. Idea

The animal behaviour Andres and I wanted to simulate was one of a scavenger. Scavengers are animals that prey on other recently dead animals that have died from causes other than predation. Depending on the hunting behavior, scavengers vary greatly in terms of the type of food they look for, as well as their hunting style. In out exercise, we wanted to imitate the hunting style of foxes. The peculiar tendency they have is to approach their prey slowly and apprehensively, measuring the prey and watching for signs of movement, and then proceeding to eat.

2. Project.

The three parts we wanted to accomplish were: checking for movement, checking for temperature and avoiding non-animal-sized objects such as walls. In the process, we decided to skip step 2 and only use ultrasonic sensors to check for movement and size. The robot, if close enough to an object, would start a process of approaching the object and moving around it, while observing it from all different angles, and after a successful measurement it would determine that the object is food. Otherwise, in case the object moved away, or the object is not one of the desired shape, the robot’s verdict would be negative, and the robot would go away and search for another potential prey.

 

3. Project production:

This project has been marked with many technical challenges, therefore multiple iterations were delivered. We started off with the basic RobotBit setup utilising an ultrasonic sensor. To accomplish part 2, we also tested a directed laser thermometer. We tried plugging it to the I2C port on the RobotBit board, however, no signal was received. Later, we had an idea to use Arduino as a converter for the thermometer. However, we have been rightfully advised against it, as the complexity of such a connection would be extremely unfeasible and possibly would not even be effective. Therefore, we decided to focus on the ultrasonic sensor only.

The initial project involved a different pattern of measurement, as we planned the animal to move in a square. Values from the ultrasonic sensor would be stored, and if there was a large enough difference in distance, the robot would turn 90 degrees.

However, this could not possibly work because after the direction of the robot changed, the values also changed, resulting in the robot endlessly moving around itself.

Later, we decided to change the movement pattern into a circular one. In this case, if at any point the sensor lost the object out of sight, the robot would stop the process immediately and decide that the object was not food.

This was a far more successful approach. Using a circular object (garbage bin lid) we got the robot to go full circle and detect food. When the object was removed, the robot acted accordingly and stopped.

However, the limitation of this project is that it needs a highly controlled environment. One would not be able to simply put it in one room with another object and let it detect food on its own. It could technically do so, but achieving this would certainly take a long time and a lot of luck. Lastly, it doesn’t work on irregularly-shaped objects, especially when there are crevices involved. However, the project to some degree successfully implements the desired behavior. One improvement suggested during presentation would be to imitate herd behavior by letting the robot call on other foxes to signal food has been found.

Code: 

from microbit import *
import robotbit

counter = 0
initMovesCount = 12
circleMovesCount = 60
radius = 125

def initialMove():
global counter
degree = 0
if (counter < 10):
degree = counter * 10
elif (counter >= 10):
degree = 100
robotbit.servo(0,90-degree)
robotbit.motor(1, 100, 50)
counter += 1
sleep(50)

def initialMoves():
global initMovesCount
for i in range(int(initMovesCount)):
display.show(robotbit.sonar(pin1))
initialMove()

def circleMove():
global radius
robotbit.motor(1, radius, 100)
robotbit.motor(4, radius * 2, 100)
sleep(100)

def circleMoves():
global circleMovesCount
d = int(robotbit.sonar(pin1))
isFood = True
counter = 0
for i in range(int(circleMovesCount)):
d = int(robotbit.sonar(pin1))
if (d < 1 or d > 50):
isNotFood()
break
circleMove()
counter += 1
if (int(counter) >= 59):
display.scroll(‘FOOD!!!’)

def foodNoticed():
global initMovesCount
global circleMovesCount
initialMoves()
circleMoves()

def isNotFood():
display.scroll(‘NAAHT’)
robotbit.motor(0, 100, 500)
robotbit.servo(0,90)
sleep(500)

while True:
d = int(robotbit.sonar(pin1))
if (d < 1 or d > 50):
foodNoticed()
else:
robotbit.motor(0, 100, 500)
robotbit.motor(1, 100, 300)
sleep(500)
#sleep((initMovesCount * 50) + (circleMovesCount + 100))

Personality Mirror // IML midterm by Konrad Krawczyk

Idea:

For a long time, I was interested in exploring the possible connections between facial expression, presentation and personality. The topic of biological determinism and the degree to which it can accurately describe the complexity of personality traits has long been a topic of a racially and politically charged discourse. The project presented below is an exploration in possibilities and limitations of such modelling, as well as an attempt to bring back right to self-reflection and self-determination to the person who is being watched.

Background:

The alleged connection between shape of human head and innate traits has been quite contentious, but this did not prevent certain ideologies to make use of its “scientific” appeal. In the 19th century, the pseudoscience of character reading named phrenology has gained enormous popularity in the Anglo – Saxon countries, particularly the United States. The growth in popularity has been engendered by the relatively recent hypothesis that the brain was the “organ of the mind”, which eventually turned out to be true – just not in the ways phrenologists would like.

As proponents of the science imagined, the brain was an organ divided into separate sections, each responsible for different kind of variance in the mental faculty. However, the evidence provided was largely derived from confirmation seeking, with little effort to account for contradictory data. Scientification of racism (the “savage”-type brain), gender and class divisions (“highbrow” vs “lowbrow” distinctions) have for decades reinforced what would otherwise stay in the domain of social conditioning, therefore in the domain of social change.

In this way, it is somewhat similar to the research being done right now in terms of personality prediction

In the areas of AI and machine intelligence, there are several startups and research groups that specialise in determining personalities from historical facial data. One of them, Faception, is claiming to have found a way to determine potential terrorists from facial data. The models developed and presented are clearly ridden with racist bias. The way most of machine learning works is through learning from historical data, which is often loaded with bias, and then making future predictions based on it. In short: no matter how “intelligent” the model is, the “garbage in, garbage out” rule still applies.

The process:

In the project, I decided to look into two specific traits: agency (defined as assertiveness and goal-orientedness) and communion (willingness to collaborate and socialise). These constitute the Big Two, which was most easy to infer.

The model is based off of a Basel Face Dataset, with labelled photos of 40 participants (50:50 gender breakdown), from whom photos have been taken and digitally altered to show variance in perceived personality. Although the dataset is relatively small, valid inferences can be made due to the fact that all samples have been validated by human subjects. Respondents have been hired through Amazon MTurk, who after being presented with photos, have answered questions about their response to the stimuli.

Due to the valid privacy concerns, it took me a week to gain access to the dataset. I had to send a request to the University of Basel, which then had to be cross-checked with the professor.

Having gained access, I started the initial exploration. It turned out that much of the variance in the faces can be found in bone structure. I looked at the photos and saw that individuals with low perceived agency have much less “sharp” facial features, which is especially visible on their eyebrows.

Therefore, I decided to use landmark detection for facial inferences. This has been generated with the OpenCV library. Then, I generated number-based labels for the personality data. It has not been generated by me. The files were labelled in the dataset, but only through their filenames and numbers. Therefore, I had to convert them into their numeric representations, ranging from 0 to 2.

Code for processing the data:

import cv2
import numpy as np
import dlib
import sys
import glob
import csv

imgs = []
path = “data/samples”
valid_images = [“.jpg”,”.gif”,”.png”,”.tga”]
detector = dlib.get_frontal_face_detector()
predictor = dlib.shape_predictor(“shape_predictor_68_face_landmarks.dat”)

landmarks_mx = []

filenames = glob.glob(“data/big_two/*.png”)
filenames.sort()
images = [cv2.imread(img) for img in filenames]

counter = 0

for image in images:
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faces = detector(gray)
landmarks_row = []
for face in faces:
landmarks = predictor(gray, face)
landmarks_values = []
for n in range(0, 68):
x = landmarks.part(n).x
y = landmarks.part(n).y
cv2.circle(image, (x, y), 2, (255, 255, 255), -1)
landmarks_values.append(x)
landmarks_values.append(y)
if (0 == (counter % 4)):
landmarks_values.append(2)
landmarks_values.append(1)
elif (1 == (counter % 4)):
landmarks_values.append(0)
landmarks_values.append(1)
elif (2 == (counter % 4)):
landmarks_values.append(1)
landmarks_values.append(2)
elif (3 == (counter % 4)):
landmarks_values.append(1)
landmarks_values.append(0)
landmarks_row.append(landmarks_values)
counter += 1
landmarks_mx.append(landmarks_row)

with open(‘landmarks_file_big_two.csv’, mode=’w’) as landmarks_file:
landmarks_writer = csv.writer(landmarks_file, delimiter=’,’, quotechar='”‘, quoting=csv.QUOTE_MINIMAL)
for row in landmarks_mx:
landmarks_writer.writerow(row)

Training:

I trained the dataset using the TensorFlow neural network, and the results were very underwhelming. Therefore, I decided to immediately switch to an open-source tool that I have used in the past, namely sklearn. There, I used models that I thought would work better with the relatively small amount of data that I got access to. Surprisingly, support vector machine has worked very well, both in agency prediction (left) and communion prediction (right).

Code for the training phase

import array as arr
import numpy as np
import os
import csv

from sklearn.preprocessing import StandardScaler
from sklearn.preprocessing import Normalizer
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.svm import SVC
from sklearn.naive_bayes import GaussianNB
from sklearn import neighbors
from sklearn.metrics import accuracy_score
from sklearn.metrics import classification_report
from sklearn.externals import joblib

batch_size = 32
num_classes = 10
epochs = 100
num_predictions = 20

class_names = [[‘Dominant’, ‘Submissive/Dominant’, ‘Submissive’ ], [‘Trustworthy’, ‘Somewhat Trustworthy’, ‘Untrustworthy’]]

big_two = []
originals = []
all_data = []

#convert the csv strings to a nice data matrix (Big Two)

with open(‘landmarks_file_big_two.csv’) as csv_file:
csv_reader = csv.reader(csv_file, delimiter=’,’, quotechar='”‘, quoting=csv.QUOTE_MINIMAL)
for row in csv_reader:
rowArr = list(map(int,row[0].replace(‘[‘, ”).replace(‘]’, ”).split(‘,’))) #map = to convert the number from string (some has also space ) to integer
big_two.append(rowArr)

with open(‘landmarks_file_originals.csv’) as csv_file:
csv_reader = csv.reader(csv_file, delimiter=’,’, quotechar='”‘, quoting=csv.QUOTE_MINIMAL)
for row in csv_reader:
rowArr = list(map(int,row[0].replace(‘[‘, ”).replace(‘]’, ”).split(‘,’))) #map = to convert the number from string (some has also space ) to integer
originals.append(rowArr)

#merge originals and big two data
all_data = big_two
i = 4
j = 0
while i <= len(big_two):
all_data.insert(i, originals[j])
i += 5
j += 1

#Prepare data for training
X = []
y = []

for row in all_data:

X.append(row[:-2])
#y.append(row[-2:-1])
y.append(row[-1:])

print(len(X))
print(len(y))

X = np.array(X)
y = np.array(y)

print(len(y))

X_train, X_test, y_train, y_test = train_test_split(X,y.ravel(),random_state=0)

# scaler = StandardScaler().fit(X_train)
# standardized_X = scaler.transform(X_train)
# standardized_X_test = scaler.transform(X_test)

#NAIVE BAYES
print(“——–NAIVE BAYES———“)
gnb = GaussianNB()
gnb.fit(X_train, y_train)
y_pred = gnb.predict(X_test)
gnb.score(X_test, y_test)
print(accuracy_score(y_test, y_pred))
print(classification_report(y_test, y_pred))

#SVM
print(“——–SUPPPORT VECTOR MACHINE———“)
svc = SVC(kernel=’linear’)
svc.fit(X_train, y_train)
y_pred = svc.predict(X_test)
svc.score(X_test, y_test)
print(accuracy_score(y_test, y_pred))
print(classification_report(y_test, y_pred))
#this is rly good save it as a pickle file
#joblib.dump(svc, ‘saved_model_communion.pkl’)

#KNN
print(“——–K-NEAREST NEIGHBORS CLASSIFIER———“)
knn = neighbors.KNeighborsClassifier(n_neighbors=5)
knn.fit(X_train, y_train)
y_pred = knn.predict(X_test)
knn.score(X_test, y_test)
print(accuracy_score(y_test, y_pred))
print(classification_report(y_test, y_pred))

Later I saved the model into a .pkl file, and rolled out an API using flask. (Repo available here https://github.com/krawc/faces-restful).

Then, I developed a front-end tool, which has been the most difficult part.

 

This is the version after refinement and style fixes. It still takes a considerable amount of time to load the model, so it might work at the second try only (depending on the connection speed)

Repo: https://github.com/krawc/faces-frontend

Demo: https://faces-frontend.herokuapp.com/

Reflection:

The tool I developed has a surprising accuracy, for the relatively small amount of unprocessed data that has been used. After having tested it, I can see that the patterns from photos are reflected in the common usage of the app. What I am most interested in, however, is how this can be related to the users’ perceptions of themselves. If I can measure how accurate a model is for “perceived” personality, it is also important to measure how this perception relates to reality, or whether at all. The interactive part could also be further developed, and design could be improved.

BIRS midterm plan // Konrad Krawczyk

The proposed bio-inspired creature we are going to build is going to take a form of a scavenger, i.e. an animal feeding off of dead animals. The creature is going to be built off of the Kittenbot setup, and programmed with Python. The sensor in use is mostly going to be the ultrasonic sensor, for movement and distance detection.

In short, the animal is going to behave in the following way:

1. Seek
2. Detect objects
3. Measure objects
4. Eat or run away

The device is going to move in seemingly random directions around a given space. When it detects an object in close proximity, the robot is going to stop for three seconds and try to detect any movement. If there is a significant enough difference between prior sensor values and current sensor values, it means a movement has been detected, and the creature will run in the opposite direction.

Otherwise, the creature will try to measure the object by navigating around it and detecting distance with the ultrasonic sensor. As the creature moves along a small object, the sensor should stop detecting it. If that’s the case, another side of that object will be measured. If that succeeds at least three times, the creature is going to move forward and signal “eating” on the screen.

BIRS: 4 Braitenberg Creatures // Konrad Krawczyk

  1. Plan

    I decided to implement three Braitenberg machines: paranoid, timid, indecisive and as a slightly more advanced one, insecure.

    I started off with just wanting to build the Insecure one, and then, I simply felt like experimenting.

    The insecure one was pretty straightforward. In case a robot bumps onto an object, it stops for a second, then turns 90 degrees either way, and moves forward again.

    Having had a bit of experience in Python, I could easily write code and organize it into functions. However, I didn’t know the specific robot bit library that the robot was using.

  2. Program

    The task started with programming the insecure robot. After having figured out how to read the data from the ultrasonic sensor, The rest was relatively easy. Perhaps, other than that, I had to get used to setting up opposite speeds on both of the servos, for the car to move unidirectionally.

    For the other three machines, I installed a basic light sensor on the breadboard, read the analog stream from the pin, and set up a conditional statement determining when the car should stop or start moving. For timid machines, the car should avoid light and stop under the shadow, for the paranoid one, it should avoid shadows (which only requires one slight change in the code) and for the indecisive machine it should move backwards from shadows and forwards when detecting bright light.

  3. Document.
  4. Code: Insecure (remixed):

    # Write your code here 🙂
    from microbit import *
    import music
    import robotbit

    display.show(Image.HAPPY)

    def forward():
    robotbit.motor(1, -100, 0)
    robotbit.motor(4, 100, 0)
    sleep(1000)

    def back():
    robotbit.motor(1, 100, 0)
    robotbit.motor(4, -100, 0)
    sleep(1000)

    while True:
    display.scroll(robotbit.sonar(pin1))
    if (robotbit.sonar(pin1) < 30):
    music.play(music.DADADADUM)
    sleep(1000)
    forward()
    sleep(100)
    else:
    robotbit.motorstop(1)
    robotbit.motorstop(4)
    robotbit.motor(0, 100, 500)

    Code: Timid and Paranoid: 

    # Write your code here 🙂
    from microbit import *
    import robotbit

    counter = 0

    def forward():
    global counter
    robotbit.motor(1, 100, 0)
    robotbit.motor(4, -100, 0)
    counter += 1
    sleep(1000)
    if (counter % 2 == 0):
    robotbit.motor(0, 100, 500)
    else:
    robotbit.motor(0, -100, 500)

    while True:
    if (pin0.read_analog() > 400): #in case of Paranoid, revert the inequality
    forward()
    else:
    robotbit.motorstop(1)
    robotbit.motorstop(4)

    Code: Indecisive

# Write your code here 🙂
from microbit import *
import music
import robotbit

display.show(Image.HAPPY)

def forward():
robotbit.motor(1, -100, 0)
robotbit.motor(4, 100, 0)
sleep(1000)

def back():
robotbit.motor(1, 100, 0)
robotbit.motor(4, -100, 0)
sleep(1000)

while True:
if (pin0.read_analog() > 400): #in case of Paranoid, revert the inequality
forward()
else:
back()

4. Analyse

Two limitations that all of these implementations share are related to the sensitivity of sensors. For three of the simple implementations, I have used a light sensor. However, the sensors behave differently in different lighting contexts, especially when exposed to daylight vs generic classroom lighting. I often had to manually adjust the thresholds in order to achieve the desired behavior.

Another aspect is the amount of delay between trigger and response. In animals, this amount of time is usually very short. In the system I worked on, however, the light sensor data is sent to the extension board, then to microbit, where the Python code is executed, and then to the servo. The delay, on average, takes a second. This is visible in many videos from the experiments I made.

5. Remix

As a remix of the code for the Insecure robot, I decided to make a robot that would perform the inverse of the Roomba-style wallflower. The robot looked around and search for objects in the vicinity, using the ultrasonic sensor. If the robot detected an object, it played a scary song, approached the object, and started bumping into it repeatedly. The idea is so utterly stupid that Linda and I used it in our Stupid Hackthon project.

6. Reflect

After some consideration, there seems to be a way to fix the aforementioned context issues with lighting. For example, instead of looking for a specific lighting threshold, the values from the sensor could be stored in a variable, and shadows / lights could be detected based on the relative difference in lighting, as opposed to a specific amount of light delivered.

Overall, Kittenbot is a great interface to work with, equipped with many useful sensors and highly expandable, therefore I will work with it for my upcoming midterm.