Introduction

When I worked on the development of a healthcare device for detecting baby Apnea (pause of breathing), I have spent some time on studying possible technological solutions. I have tried several different types of sensors including: RGB-camera, 3-axis accelerometer, heat camera, depth camera (kinect), etc. The rgb-camera-based solution is very interesting. Firstly because they are low-cost; moreover, it does not require mounting the sensor on baby’s body thus the comfort is guaranteed. That’s why I worked for about 3 weeks on this solution.

As experiments, I tried multiple possible algorithms including: feature points tracking, inter-frame difference checking, flux, and color nuance variation tracking.

In this post, I want to present the latter one. This work is inspired by this study. The basic idea is to track the color variation of an ROI on human face. The blood circulation and breathing will cause slight color variation of our skin. This change is so slight that our eyes can not perceive it at all. However, a normal webcam can identify color variation with a resolution as high as 2^16 grades.

ROI on human forehead

I have chosen a small rectangle area as ROI (region of interest) for tracking color variation. Below is the part of code for this purpose.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
x1 = 0.4
x2 = 0.6
y1 = 0.1
y2 = 0.25
face_cascade=cv2.CascadeClassifier(cv2.data.haarcascades + "haarcascade_frontalface_default.xml")

def getFaceROI(img):
gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.2, 5)

if len(faces)>0:
img=cv2.rectangle(img, (faces[0][0] + int(x1*faces[0][2]), faces[0][1]+ int(y1*faces[0][3]) ), (faces[0][0]+ int(x2*faces[0][2]), faces[0][1]+ int(y2*faces[0][3]) ), (255, 0, 0), 2)

return [faces[0][0]+int(x1*faces[0][2]), faces[0][1]+ int(y1*faces[0][3]), faces[0][0]+ int(x2*faces[0][2]), faces[0][1]+ int(y2*faces[0][3])]
else:
return [0,0,0,0]

Color Calculation

We simply calculate the average value of a given color channel (I used the green channel in my test) of all the pixels in the selected ROI.

1
2
def getColorAverage(frame, color_id):
return frame[:,:,color_id].sum()*1.0 / (frame.shape[0]*frame.shape[1])

Drawbacks

Entire code has been uploaded to github repo below:
https://github.com/QiuZhaopeng/CV2_heartbeat_breathing_detection

This solution suffers from limitations in multiple aspects, e.g. the lighting condition, human should stay still, the camera noisy level, etc. That’s why I did not choose this solution for our product (finally we chose the 6-axis sensor solution).

Result

Below is the snapshot of the execution of this program. As one can see, there are low-frequency wave shape mixed with high-frequency sawtooth (small peaks).
Alt Text
I measured my pulses during execution of this program. It can be verified that each small peak corresponds to a pulse beat (heart beat). And the large peak-valley transition corresponds to breathing cycles.

Anyone interested in this small experiment are encouraged to try it and do some further work. Good luck.

I am working on migration of our Data Warehouse project from Alibaba Cloud to Huawei Cloud this week. All goes well, except one issue occurs when I tried to connect to MongoDB using MongoDB Compass Community.

As I have presented in my previous post, I have deployed MongoDB service using docker and I have successfully connected to my MongoDB database from IPython console. So I firstly tried again the visit from IPython today to make sure that the MongoDB service is still running on Huawei ECS. It IS still working well:

Alt Text

Then I open MongoDB Compass Community and fill the login information as below (our domain name has not yet been authorized so I have to use Ip address for hostname):
Alt Text
However, when I click “CONNECT” button, nothing happens.

After a close investigation, I found the reason. After I filled all the fields for connection, Compass yielded a connection URI string as below.
Alt Text

In fact, as explained in official manual of MongoDB, the standard URI syntax for connection is as:
mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]].
Thus in my case, MongoDB Compass has strangely used ip:port as the Database name to visit. Since there is no database with such name, so it shall do nothing and stays in the connection interface.

Now I correct the URI string manually and fill my database name suppliers into this string as below:
Alt Text

Once I click “CONNECT”, Compass jumps as expected to display interface of the queried database now:
Alt Text

I started learning JavaScript since November 2018. Since then, I fell in love with this powerful language. In this post, I would like to share my experience of app dev using JavaScript and to encourage you guys to develop apps using HTML5 tech.

My first experience with JS started with MUI by using which I aimed to develop an mobile application to measure human abdomen movements using smartphone’s accelerator sensor. MUI is a H5-based framework for developing “native” applications (can be called Chinese-version Reaction Native?). The MUI team has also developed HTML5plus sdk (Chinese-version Cordova) which grants applications the access to host device’s capabilities such as sensors, phone status, etc. With these tools, I have easily developed my application within two days. One month later, we have decided to develop an application for communicating with our smart wearable hardware via Bluetooth. Thus along with progress of hardware development, I have developed another application for testing communications with the hardware and for collecting test data. Several months later, this application becomes a very useful tool for our team, which is used to: flash the firmware, testing the stability of BLE connection, adjusting parameters for algorithm and embedded software, collecting debug logs, data collecting during nightly tests, ROB (robustness) issues investigation, etc.

For copyright reason, I cannot share the source code of this application. Below are screenshots of two non-secret parts of my code. The bluetooth dev manual is given here.

Alt Text
Alt Text

This is a video showing how I did the test with my application and the wearable hardware prototype.

Another video of motion data collection on a premature baby using my app:

Recently I started to do exercise with a new RL method: Actor-Critic. The basic idea is quite similar to GAN network. Unlike Policy Gradient in which a single neural network is trained, Actor-Critic uses two neural networks: an “actor” for performing actions and a “critic” for evaluating action of the actor. I refer to this page for details of this method.

Model presentation

Given a state s_t at time t, the network calculates the action probabilities and chooses an action a_t to apply on env:
Alt Text

After the application of action a_t, then env updated accordingly, yields reward r_t, we observe at next time step t+1 a new state s_t+1 which results in a new sampled action a_t+1:
Alt Text

Steps of the basic Actor-Critic method

Unlike the Policy Gradient which needs to gather all data of an episode to calculate gradients, Actor-Critic method performs model update in every agent-env interaction loop :

Alt Text

Implementation with CartPole game

I followed this youtube video for my first Actor-Critic exercise. It was initially based on pure Keras thus I have modified some of his code for using tensorflow. I also disabled eager mode because of a runtime error (I am using Tensorflow v2 on my laptop).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import tensorflow as tf
tf.compat.v1.disable_eager_execution()

from tensorflow.keras import backend as K
from tensorflow.keras.layers import Activation, Dense, Input

from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam

import numpy as np
import gym

class Agent(object):
def __init__(self, alpha, beta, gamma=0.99, n_actions=2, \
layer1_size=1024, layer2_size=512, input_dims=4):
self.gamma = gamma
self.alpha = alpha
self.beta = beta
self.input_dims = input_dims
self.fc1_dims = layer1_size
self.fc2_dims = layer2_size
self.n_actions = n_actions

self.actor, self.critic, self.policy = self.build_network()

self.action_space = [i for i in range(self.n_actions)]


def build_network(self):
input = Input(shape=(self.input_dims, ))
delta = Input(shape=[1])
dense1 = Dense(self.fc1_dims, activation="relu")(input)
dense2 = Dense(self.fc2_dims, activation="relu")(dense1)
probs = Dense(self.n_actions, activation="softmax")(dense2)
values = Dense(1, activation="linear")(dense2)

def custom_loss(y_true, y_pred):
out = K.clip(y_pred, 1e-8, 1-1e-8)
log_lik = y_true * K.log(out)
return K.sum(-log_lik*delta)

actor = Model(inputs=[input, delta], outputs=[probs])
actor.compile(optimizer=Adam(lr=self.alpha), loss=custom_loss)

critic = Model(inputs=[input], outputs=[values])
critic.compile(optimizer=Adam(lr=self.beta), loss="mse")

policy = Model(inputs=[input], outputs=[probs])

return actor, critic, policy

def choose_action(self, observation):

state = observation[np.newaxis, :]
probs = self.policy.predict(state)[0]

action = np.random.choice(self.action_space, p=probs)

return action

def learn(self, state, action, reward, state_, done):
state = state[np.newaxis, :]
state_ = state_[np.newaxis, :]
critic_value_ = self.critic.predict(state_)
critic_value = self.critic.predict(state)

target = reward + self.gamma*critic_value_*(1-int(done))
delta = target - critic_value

actions = np.zeros([1, self.n_actions])
actions[np.arange(1), action] = 1.0

self.actor.fit([state, delta], actions, verbose=0)
self.critic.fit(state, target, verbose=0)


if __name__ == "__main__":
agent=Agent(alpha=0.0001, beta=0.0005)
env = gym.make("CartPole-v0")
score_history = []
num_episodes = 2000

for i in range(num_episodes):
done = False

score = 0
observation = env.reset()

while not done:
env.render()
action = agent.choose_action(observation)
observation_, reward, done, info= env.step(action)
agent.learn(observation, action, reward, observation_, done)
observation = observation_
score+=reward

score_history.append(score)
avg_score = np.mean(score_history[-100:])
print("episode ", i, "score %.2f average score %.2f" % (score, avg_score))

A newer version

I have updated the program shown above to allow eager mode. The main difference is the introduction of the custom loss function for actor. It works well, however, the running speed is much slower after my modification (I am looking forward to Refactoring it later).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125

import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = "2"


import tensorflow as tf

if tf.__version__.startswith("1."):
raise RuntimeError("Error!! You are using tensorflow-v1")

from tensorflow.keras import backend as K
from tensorflow.keras.layers import Activation, Dense, Input

from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adam

import numpy as np
import gym

class Agent(object):
def __init__(self, alpha, beta, gamma=0.99, n_actions=2, \
layer1_size=1024, layer2_size=512, input_dims=4):
self.gamma = gamma
self.alpha = alpha
self.beta = beta
self.input_dims = input_dims
self.fc1_dims = layer1_size
self.fc2_dims = layer2_size
self.n_actions = n_actions

self.actor, self.critic, self.policy = self.build_network()

self.action_space = [i for i in range(self.n_actions)]

self.optimizer = tf.keras.optimizers.Adam(learning_rate=self.alpha)

def build_network(self):

input = Input(shape=(self.input_dims, ))
delta = Input(shape=[1])
dense1 = Dense(self.fc1_dims, activation="relu")(input)
dense2 = Dense(self.fc2_dims, activation="relu")(dense1)
probs = Dense(self.n_actions, activation="softmax")(dense2)
values = Dense(1, activation="linear")(dense2)

actor = Model(inputs=[input, delta], outputs=[probs])

critic = Model(inputs=[input], outputs=[values])
critic.compile(optimizer=Adam(lr=self.beta), loss="mse")

policy = Model(inputs=[input], outputs=[probs])

return actor, critic, policy

def choose_action(self, observation):

state = observation[np.newaxis, :]
probs = self.policy.predict(state)[0]

action = np.random.choice(self.action_space, p=probs)

return action

def learn(self, state, action, reward, state_, done):
state = state[np.newaxis, :]
state_ = state_[np.newaxis, :]
critic_value_ = self.critic.predict(state_)
critic_value = self.critic.predict(state)

target = reward + self.gamma*critic_value_*(1-int(done))
delta = target - critic_value

actions = np.zeros([1, self.n_actions])
actions[np.arange(1), action] = 1.0

self.critic.fit(state, target, verbose=0)

with tf.GradientTape() as tape:
y_pred = self.actor(state)
out = K.clip(y_pred, 1e-8, 1-1e-8)
log_lik = actions * K.log(out)
myloss = K.sum(-log_lik*delta)
grads = tape.gradient(myloss, self.actor.trainable_variables)

self.optimizer.apply_gradients(zip(grads, self.actor.trainable_variables))

if __name__ == "__main__":
agent=Agent(alpha=0.0001, beta=0.0005)

ENV_SEED = 1024 ## Reproducibility of the game
NP_SEED = 1024 ## Reproducibility of numpy random
env = gym.make('CartPole-v0')
env = env.unwrapped # use unwrapped version, otherwise episodes will terminate after 200 steps
env.seed(ENV_SEED)
np.random.seed(NP_SEED)


### The Discrete space allows a fixed range of non-negative numbers, so in this case valid actions are either 0 or 1.
print(env.action_space)
### The Box space represents an n-dimensional box, so valid observations will be an array of 4 numbers.
print(env.observation_space)
### We can also check the Box’s bounds:
print(env.observation_space.high)
print(env.observation_space.low)

score_history = []
num_episodes = 2000

for i in range(num_episodes):
done = False

score = 0
observation = env.reset()

while not done:
env.render()
action = agent.choose_action(observation)
observation_, reward, done, info= env.step(action)
agent.learn(observation, action, reward, observation_, done)
observation = observation_
score+=reward

score_history.append(score)
avg_score = np.mean(score_history[-100:])
print("episode ", i, "score %.2f average score %.2f" % (score, avg_score))

The training converges ideally. Below is a snapshot of the execution output:

Alt Text

References:

*https://github.com/wangshusen/DRL
*https://www.youtube.com/watch?v=2vJtbAha3To

This weekend I found in my old hard disk a tiny game I wrote years ago. I was learning motion planning algorithms in that period, thus this game was one exercise of mine for testing the A* search algorithm (see introduction). This algorithm is a very traditional one thus I will not present it in my post. One who is interested can read [this book] (http://lavalle.pl/planning/) By Steven M. LaValle for details.

Number tiles Game in Visual Studio

I developed the sliding number tiles game (shown below) as a small desktop software using Visual Studio.
Alt Text

This video shows how I solve the game manually (I am not quite good at it):

Solving it using A*

Now I touch “S”-key of my keyboard, and the A* algorithm starts to solve the puzzle (search a path). And I push “S”-key again, it starts to show the solution.

The code has been pushed to this repo.

Below are simple explanations of the algorithm in this example.

Algorithm explanation

Node and graph

Each configuration of this game corresponds to a node in its state graph as shown below. So in order to solve this game, we need to find a path that links node start to node target via a sequence of intermediate nodes.
Alt Text

Assumptions

We assure that the assumptions are satisfied in this example:

  1. there are limited number of states;
  2. there is a valid heuristic function for assessing the distance between nodes: sum of distance (in number of moves) for each tile from its current position to its target position

Some conceptions:

Closed set: a set of nodes that has been assessed
Frontier set: a set of nodes that are about to be explored

StateNode class

Each state node object in the code has value members:
parent: a node from which current node comes with one move
g: path cost from start node to current node
h: distance cost from current node to target node
isExpanded: a flag noting if all neighbors of this node are in Closed set

Algorithm pseudo-code details:

Init: put start node into closed set and frontier set

Explore_node for a given node:

I have developed the core function of this algorithm explore_Tree(n_i) and it do the following works in a recursive way:
(1) for each neighbor nodes n_k of node n_i, check firstly if n_k is already included in closed set.
(2) If we get YES for step 1, and if current cost of n_k (namely: g+h) is lower than its cost in the closed set, update its cost and update its parent as n_i;
(3) If we get NO for step 1: put current node n_k into frontier and closed set, calculate its cost, define n_k‘s parent as n_i
(4) delete n_i from frontier set
(5) check if any node in frontier has reached target node
(6) if target reached, return True
(7) if target not yet reached, select node of the lowest cost from frontier set, and call function explore_Tree() for it.

If Explore_node finally returns True, it means that the optimal path is found. We can then retrieve the path by tracking recursively the parent, starting from the target node.

Illustration

The following image shows the first step of this algorithm: exploring neighbors of the start node and assessing the cost of each node.
Alt Text

Following the step shown above, the image below shows the how A* explores recursively the “optimal” move. In this step, it locates the lowest-cost node in frontier then explores its neighbors and assesses their cost.

Alt Text

Several days ago, a friend of mine contacted me and asked me the feasibility of a technical solution for a 3d human character simulation in HTML5 environment. He sent me this article which presents how to create an interactive 3d character with Three.js**. He is expecting to control the character’s real-time motion (whole-body motion) via a hardware such like a joystick.

It’s a very interesting work and it seems quite easy. Thus I have done a little dev work trying to make it work.

Alt Text

In file Index.html, I have defined a websocket server and an according message parser. This file then is wrapped in Electron window so it runs as a desktop software.
Core part of this Index.html is the websocket communication part as below:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
<script  type="text/javascript" >
var angle1 = 0.0;
var angle2 = 0.0

const qEvent = new Event('qiu');

/* for debug */
function output(s)
{
var out = document.getElementById("debug-area");
out.innerText += s;
}

output("Start running")


var msg_ready = false;
var msg_reading = false; // True: package head 0xAA is received, but 0x7f has not yet been received
var msg_data_buffer = [];
var msg_lenth = 0;


function processMsg(v)
{
if (v[0] == 170) // detect the beginning byte of a message: 0xAA
{
// data are sent in little endian,
// v.buffer is a byte-array and Int16Array(v.buffer, 8, 1) means that it parses from the 8th byte on to get ONE Int16 number

if ( (v[1] == 0x01) && (v[2] == 0x53) ) // 01 52
{
angle1 = new Int16Array(v.buffer, 8, 1)[0];
angle2 = new Int16Array(v.buffer, 10, 1)[0];
var temp3 = new Int16Array(v.buffer, 12, 1)[0];
document.dispatchEvent(qEvent);

}
else
{
}
}
}


var ws = require("nodejs-websocket");
var clients = new Array();
output("开始建立连接... ");
var count = 0;
var data = new Buffer.alloc(0);
var server = ws.createServer(function(conn){

conn.id = count;
count += 1;
clients["conn"+count] = conn;

conn.on("text", function (str) {
output("Received " + str + "! " )
var typeId = str.charAt(0);
conn.sendText('Success!');
})
conn.on("close", function (code, reason) {
output("Connection closed!")
//clients.delete(conn);
});

conn.on("binary", function (inStream) {

inStream.on("readable", function () {
var newData = inStream.read();

if (newData)
data = Buffer.concat([data, newData], data.length + newData.length);
});

inStream.on("end", function () {

if(data){
var t = '', v = new Uint8Array(data);

for (var i = 0; i < v.length; i++)
{
// packet head 0xAA reached, now start reading the data flow
if ((!msg_reading ) &&(v[i] == 0xaa)){
msg_reading = true;
}


if(msg_reading){

if (msg_data_buffer.length == 8) {
msg_lenth = msg_data_buffer[5]*16 + msg_data_buffer[4]; // parsing the data length (bytes size)
}

// received the end of packet, and the length is correct
if ((v[i] == 127 ) && (msg_data_buffer.length == (msg_lenth + 10))) // 10 extra bytes contained in this package for : length, scope, checksum, msg-id
{
var msg = new Uint8Array(msg_data_buffer);
processMsg(msg);
msg_data_buffer = [];
msg_reading = false;
msg_lenth = 0;
} else if (msg_data_buffer.length == (msg_lenth + 10))
{
msg_data_buffer = [];
msg_reading = false;
msg_lenth = 0;
output("Message length error!");
}
else{
msg_data_buffer.push(v[i]);
}
}
}

}else{

};
data = new Buffer.alloc(0);
conn.sendText('Binary Received!');
});


});
conn.on("message", function (code, reason) {
output("message! " )
});
conn.on("error", function (code, reason) {
output("Error occurs!")
});
}).listen(9999)
output("Server is ready! ");
</script>

In existing file script.js, I have defined function moveOneJoint(). For the moment for test only, this function change only the orientation of the head (namely neck joint). It will be called each time an event ‘qiu’ is dispatched.

1
2
3
4
5
6
7
8
9
10
11
12
13
14

document.addEventListener('qiu', function (e) {

if (neck && waist) {
moveOneJoint(neck, angle1, angle2);
}
});

function moveOneJoint(joint, x, y) {

joint.rotation.y = THREE.Math.degToRad(x);
joint.rotation.x = THREE.Math.degToRad(y);

}

Entire code has been pushed to github repo:
https://github.com/QiuZhaopeng/3d_character_simulation

I do not have a joystick so I simulate it with several range sliders in another web app (developed using MUI framework with HBuilder). By sliding the sliders, we can send the angle data via websocket to above-mentioned 3d character simulator. Data massage to be sent should be a dataarray like: [0xAA, 0x01,0x53, 0x01, 0x04,0x00,0x00,0x00, 0xMM,0xNN, 0xSS,0xTT, 0xYY,0xZZ, 0x7F] where 0xMM,0xNN and 0xSS,0xTT are angle values in Int16 and 0xYY,0xZZ can be any bytes (designed to be checksum, but I am not checking it in my code).

Below is a demo I’ve recorded. I am controlling the motion of the simulated 3d character’s head using sliders:

In another trial, I run my device simulator app on Android platform and run Electron in full screen. Check out the demo :

Context

A super fan of Matlab, I’ve used it (mainly the Optimization toolbox) a lot during my PhD study. Later on, after I started my professional job in INRIA and GE, I used mainly C++/C/C#/Python but rarely Matlab. Though Matlab is great, it is, however, too expensive for personal users. That’s why I use often open-source tools such like Numpy, Scipy, Eigen and Maxima in some math-related development works.

I heard the name of Julia in 2017. I felt astonished to learn that this new language combines the interactivity and computational power of Python/Matlab with the speed of C. It is until winter of 2020 that I have time to learn intensively this powerful and promising language. So I would like to share my learning notes here. Stay hungry!

Alt Text

Numbers

For Number type of Julia, this table in figure below presents the common basic types:
Alt Text

I have made a “Knowldge graph” of all data types in Julia and their relations. Below is a chart of all types (nodes) that are related to Numbertype:
Alt Text

My exercise code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#     one '#' symbole for a one-line comment, equal to // in C/C++

#= '#=' + '=#' pair for multi-line comment
similar to '/*'+'*/' pair in C/C++
=#

## as in java, println(xxx) is equivalent to print(xxx + '\n')


######## day 1: Numbers
a = 1
println("a: ", a, ",", typeof(a)) ## Int64 if running on 64-bit CPU, Int32 for 32-bit CPU

aa = UInt8(1) # method to convert to a specific type
println(a == aa)
println(sizeof(a), ", " ,sizeof(aa))

b = 3.2
println("b: ", b, ",", typeof(b))
println(sizeof(b))

c = 1e18
println("c: ", c, ",", typeof(c))

d = 0x3f
println("d: ", d, ",", typeof(d))

e = 0b10
println("e: ", e, ",", typeof(e))

f = 0o057
println("f: ", f, ",", typeof(f))


g = 'x'
println("g: ", g, ",", typeof(g))
println("g-1: ", g-1, ",", typeof(g-1))
println(g == UInt8('x'))
g= convert( UInt8, g) # convert function for converting types
println(g == UInt8('x'))

h = 2 + 3im
println("h: ", h, ", ", typeof(h))


i = 0.5f
println("i: ", i, ", ", typeof(i))

j = Inf - Inf
println("j: ", j, ", ", typeof(j))

k = Inf * 0
println("k: ", k, ", ", typeof(k))

l = 2/0
println("l: ", l, ", ", typeof(l))


m = 6//9

println("m: ", m, ", ", typeof(m))

n = bitstring(65535)
println("n: ", n, ", ", typeof(n))

Below is the output that I got:
Alt Text

Official Website:
https://julialang.org/

Cheat sheet:
https://juliadocs.github.io/Julia-Cheat-Sheet/
https://juliadocs.github.io/Julia-Cheat-Sheet/zh-cn/

Hello 2021

Stay confident and keep fighting

I have browsed this video on youtube homepage by chance today: https://www.youtube.com/watch?v=yYUN_k36u5Q. This tutorial talks about building a GAN (Generative Adversarial Nets) network using PyTorch and it is quite interesting and simple. Thus I cloned his github source file and updated it a little bit so that it runs with Tensorflow 2.

In this example here in my post, a REAL artist can draw wonderful “paintings” of his own style, which are parabolas that are inside the gray area in figure below and are parallel to the blue and red parabolas.
Alt Text

Then we introduce a GAN network where a generator and a discriminator are defined. The Generator model shall try to simulate the oeuvres of the artist as well as possible. The structure of this Generator model is as shown below:
Alt Text

The Discriminator model, on contrary, shall do its best to distinguish the generated paintings and the REAL paintings. Its structure is as shown below:
Alt Text

Now let’s begin to code.

Firstly the dependencies. One should have tensorflow 2, numpy and matplotlib installed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = "2"

import tensorflow as tf2
import tensorflow.keras as keras
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Activation
import tensorflow.keras.backend as K

import numpy as np
import matplotlib.pyplot as plt

print("TF version: ", tf2.__version__)

Some important hyper parameters.

1
2
3
4
5
6
7
# Hyper Parameters
BATCH_SIZE = 32
LR_G = 0.0001 # learning rate for generator
LR_D = 0.0001 # learning rate for discriminator
N_IDEAS = 5 # think of this as number of ideas for generating an art work (Generator)
ART_COMPONENTS = 15 # it could be total point G can draw in the canvas
PAINT_POINTS = np.vstack([np.linspace(-1, 1, ART_COMPONENTS) for _ in range(BATCH_SIZE)])

This function will return painting works of the real artist:

1
2
3
4
5
6
 # return painting of the famous artist (real target)
def artist_works():
a = np.random.uniform(1, 2, size=BATCH_SIZE)[:, np.newaxis]
paintings = a * np.power(PAINT_POINTS, 2) + (a-1)
# print("Shape of paitings: ", paintings.shape)
return paintings

Now the models as well as their optimizers.

1
2
3
4
5
6
7
8
9
10
11
12
model_G = Sequential([
Dense(128, activation="relu",input_shape=(None, N_IDEAS)),
Dense(ART_COMPONENTS)
])

model_D = Sequential([
Dense(128, activation="relu", input_shape=(None, ART_COMPONENTS)),
Dense(1, activation="sigmoid"),
])

opt_D = keras.optimizers.Adam(model_D.variables, lr=LR_D)
opt_G = keras.optimizers.Adam(model_G.variables, lr=LR_G)

Finally the training part and the plot stuffs.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
          
plt.ion() # something about continuous plotting

for step in range(10000):
artist_paintings = artist_works() # real painting from artist
G_ideas = np.random.randn(BATCH_SIZE, N_IDEAS)
with tf2.GradientTape(persistent=True) as tape:
G_paintings = model_G(G_ideas) # fake painting from model G (random ideas)
prob_artist1 = model_D(G_paintings) # model D computes the prob for assessing if G_paintings are real
G_loss = K.mean(K.log(1. - prob_artist1)) # loss function of G model


prob_artist0 = model_D(artist_paintings) # D try to increase this prob for real paitings
prob_artist1 = model_D(G_paintings) # D try to reduce this prob for generated paitings
D_loss = - K.mean(K.log(prob_artist0) + K.log(1. - prob_artist1)) # loss function of G model

L_gradx = tape.gradient(G_loss, model_G.variables)
opt_G.apply_gradients(grads_and_vars=zip(L_gradx, model_G.variables))

L_gradx2 = tape.gradient(D_loss, model_D.variables)
opt_D.apply_gradients(grads_and_vars=zip(L_gradx2, model_D.variables))


if step % 50 == 0: # plotting
plt.cla()
plt.plot(PAINT_POINTS[0], G_paintings[0], c='#4AD631', lw=3, label='Generated painting',)
plt.plot(PAINT_POINTS[0], 2 * np.power(PAINT_POINTS[0], 2) + 1, c='#74BCFF', lw=3, label='upper bound')
plt.plot(PAINT_POINTS[0], 1 * np.power(PAINT_POINTS[0], 2) + 0, c='#FF9359', lw=3, label='lower bound')
plt.text(-.5, 2.3, 'D accuracy=%.2f (0.5 for D to converge)' % K.mean(prob_artist0), fontdict={'size': 13})
plt.text(-.5, 2, 'D score= %.2f (-1.38 for G to converge)' % -D_loss, fontdict={'size': 13})
plt.ylim((0, 3));plt.legend(loc='upper right', fontsize=10);plt.draw();plt.pause(0.01)
print("Current step: {}".format(step))
plt.ioff()
plt.show()

See how the simulated painting evolves:

Alt Text

As we can see, as the training steps increases, the “generated paintings” in green becomes more and more real: it becomes quite parallel to upper and lower limit parabolas and stays inside the limits.

Map / Reduce in Python

To understand the concept, please refer to this paper of Google: link

In python 2, map() and reduce() are both built-in functions. However, since Python3, reduce() is no longer a built-in function, one should import it from functools module.
Alt Text

map() applies a function to all the items in an input list and returns an iterator object. Such like:

1
2
3
>> a = map(lambda x:x+x, [1,2,3,4,5])
>> list(a)
Out: [2, 4, 6, 8, 10]

reduce() performs a rolling computation to sequential pairs of values in an input list and returning the result.
Alt Text

1
reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

Here is an example written by myself today:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
from functools import reduce # only in Python 3

DIGITS = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9,'.': -1}

def char2num(s):
return DIGITS[s]

def str2float(s):
L = map(char2num, s)
r = reduce(func,L)
print(r)
return r[0]

def func(x, y):

if type(x) == int:
if x == -1:
return (0 + y, 0.1)
elif y == -1:
return(x, 0.1)
else:
return (x*10+y, 1)
elif type(x) == tuple:

if y == -1:
return(x[0], 0.1)
else:
if x[1]< 1:
res = x[1]*0.1
return(x[0]+y*x[1], res)
else:
res = 1
return(x[0]*10+y, res)

if __name__ == "__main__":
#print('str2float(\'123.456\') =', str2float('123.456'))
if abs(str2float('123.4') - 123.4) < 0.00001:
print('测试成功!')
else:
print('测试失败!')

if abs(str2float('123456') - 123456) < 0.00001:
print('测试成功!')
else:
print('测试失败!')

if abs(str2float('0.123456') - 0.123456) < 0.00001:
print('测试成功!')
else:
print('测试失败!')

reference:

  1. https://www.liaoxuefeng.com/wiki/1016959663602400/1017329367486080
  2. https://book.pythontips.com/en/latest/map_filter.html