playground-data 1.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- playground_data-1.2.0.dist-info/METADATA +208 -0
- playground_data-1.2.0.dist-info/RECORD +14 -0
- playground_data-1.2.0.dist-info/WHEEL +5 -0
- playground_data-1.2.0.dist-info/licenses/LICENSE +201 -0
- playground_data-1.2.0.dist-info/top_level.txt +1 -0
- plygdata/__init__.py +25 -0
- plygdata/_version.py +1 -0
- plygdata/datacolor.py +96 -0
- plygdata/datahelper.py +236 -0
- plygdata/dataset.py +268 -0
- plygdata/heatmap.py +89 -0
- plygdata/playground.py +182 -0
- plygdata/scalelinear.py +81 -0
- plygdata/state.py +72 -0
plygdata/datahelper.py
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# Copyright 2018-2019 Digital Advantage Co., Ltd. All Rights Reserved.
|
|
3
|
+
#
|
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
# you may not use this file except in compliance with the License.
|
|
6
|
+
# You may obtain a copy of the License at
|
|
7
|
+
#
|
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
#
|
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
# See the License for the specific language governing permissions and
|
|
14
|
+
# limitations under the License.
|
|
15
|
+
# ==============================================================================
|
|
16
|
+
|
|
17
|
+
from __future__ import division
|
|
18
|
+
|
|
19
|
+
import matplotlib.pyplot as plt
|
|
20
|
+
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
|
21
|
+
import numpy as np
|
|
22
|
+
from plygdata.dataset import generate_data
|
|
23
|
+
from plygdata.state import InputType
|
|
24
|
+
from plygdata.heatmap import HeatMap
|
|
25
|
+
from plygdata.playground import Player
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
POINT_DOMAIN = [-6.0, 6.0]
|
|
29
|
+
TICKS_POINT = [-6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6]
|
|
30
|
+
TICKS_MIDDLE = 6
|
|
31
|
+
TICKS_VALUE = [-1, 0, 1]
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def split_data(data, validation_size=0.5, label_num=1, training_size=-1.0):
|
|
35
|
+
'''
|
|
36
|
+
Split data int training and validation data. And Each data will be split into input data and teacher labels.
|
|
37
|
+
:param data:
|
|
38
|
+
:param validation_size:
|
|
39
|
+
:param label_num:
|
|
40
|
+
:param training_size:
|
|
41
|
+
:return: (X_train, y_train, X_valid, y_valid)
|
|
42
|
+
'''
|
|
43
|
+
|
|
44
|
+
len_data = len(data)
|
|
45
|
+
if len_data == 0:
|
|
46
|
+
raise ValueError("No array error.")
|
|
47
|
+
|
|
48
|
+
mat = np.array(data)
|
|
49
|
+
|
|
50
|
+
# It is easy to use scikit-learn.
|
|
51
|
+
# X = mat[:,:-(label_num)] # data
|
|
52
|
+
# y = mat[:,-(label_num)] # label
|
|
53
|
+
# from sklearn.model_selection import train_test_split
|
|
54
|
+
# X_train, X_valid, y_train, y_valid = train_test_split(X, y, test_size=validation_size)
|
|
55
|
+
|
|
56
|
+
# But, because this class doesn't use scikit-learn.
|
|
57
|
+
np.random.shuffle(mat)
|
|
58
|
+
|
|
59
|
+
X = mat[:, :-(label_num)] # data
|
|
60
|
+
y = mat[:, -label_num:] # labels
|
|
61
|
+
|
|
62
|
+
if training_size >= 0.0 and training_size <= 1.0:
|
|
63
|
+
validation_size = 1.0 - training_size
|
|
64
|
+
|
|
65
|
+
split_point = round(len(mat) * (1.0 - validation_size))
|
|
66
|
+
X_train = X[0:split_point, :] # train data
|
|
67
|
+
y_train = y[0:split_point] # train label
|
|
68
|
+
X_valid = X[split_point:, :] # validation data
|
|
69
|
+
y_valid = y[split_point:] # validation label
|
|
70
|
+
|
|
71
|
+
if label_num == 1:
|
|
72
|
+
y_train = np.reshape(y_train, [len(y_train), 1])
|
|
73
|
+
y_valid = np.reshape(y_valid, [len(y_valid), 1])
|
|
74
|
+
|
|
75
|
+
# To Pandas example:
|
|
76
|
+
# import numpy as np
|
|
77
|
+
# import pandas as pd
|
|
78
|
+
# df_X_train = pd.DataFrame(X_train)
|
|
79
|
+
# df_X_train.columns = ['x1', 'x2']
|
|
80
|
+
# print(df_X_train)
|
|
81
|
+
# df_y_train = pd.DataFrame(y_train)
|
|
82
|
+
# df_y_train.columns = ['label']
|
|
83
|
+
# print(df_y_train)
|
|
84
|
+
|
|
85
|
+
return X_train, y_train, X_valid, y_valid
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def get_playground_figure(enable_colorbar=False):
|
|
89
|
+
if enable_colorbar:
|
|
90
|
+
fig = plt.figure(figsize=(6, 6), dpi=100)
|
|
91
|
+
else:
|
|
92
|
+
fig = plt.figure(figsize=(5, 5), dpi=100)
|
|
93
|
+
return fig
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def get_playground_axes(fig):
|
|
97
|
+
ax = fig.add_subplot(111) # Row: 1, Column: 1, Place: 1
|
|
98
|
+
ax.set_facecolor("#e8eaeb")
|
|
99
|
+
ax.spines['top'].set_visible(False)
|
|
100
|
+
ax.spines['right'].set_visible(False)
|
|
101
|
+
ax.spines['bottom'].set_visible(False)
|
|
102
|
+
ax.spines['left'].set_visible(False)
|
|
103
|
+
ax.yaxis.tick_right()
|
|
104
|
+
ax.tick_params(axis='x', colors='#bbbbbb')
|
|
105
|
+
ax.tick_params(axis='y', colors='#bbbbbb')
|
|
106
|
+
ax.set_xlim(POINT_DOMAIN)
|
|
107
|
+
ax.set_ylim(POINT_DOMAIN)
|
|
108
|
+
ax.set_xticks(TICKS_POINT)
|
|
109
|
+
ax.set_yticks(TICKS_POINT)
|
|
110
|
+
ax.get_xticklabels()[TICKS_MIDDLE].set_color("#333333")
|
|
111
|
+
ax.get_yticklabels()[TICKS_MIDDLE].set_color("#333333")
|
|
112
|
+
#ax.grid(color='white', linestyle='-')
|
|
113
|
+
ax.grid(visible=False)
|
|
114
|
+
return ax
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def plot_points(ax, X_train, y_train, X_valid=None, y_valid=None):
|
|
118
|
+
|
|
119
|
+
HeatMap.updateTrainPoints(ax, X_train, y_train)
|
|
120
|
+
|
|
121
|
+
if all([X_valid is not None, y_valid is not None]):
|
|
122
|
+
HeatMap.updateValidationPoints(ax, X_valid, y_valid)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def plot_points_with_playground_style(X_train, y_train, X_valid=None, y_valid=None, figsize=(5, 5), dpi=100):
|
|
126
|
+
"""
|
|
127
|
+
if width 5 inches x height 5 inches x dpi 100, = 500 x 500 dots figure will be created.
|
|
128
|
+
|
|
129
|
+
:param X_train: train data
|
|
130
|
+
:param y_train: grain label
|
|
131
|
+
:param X_valid: validation data
|
|
132
|
+
:param y_valid: validation label
|
|
133
|
+
:param figsize: width inches (default: 5) x height (default: 5) inches
|
|
134
|
+
:param dpi: dpi (default: 100)
|
|
135
|
+
:return: a Figure object of matplotlib, and the axes (Coordinate axis)
|
|
136
|
+
"""
|
|
137
|
+
fig = plt.figure(figsize=figsize, dpi=dpi)
|
|
138
|
+
|
|
139
|
+
# get the axes (Coordinate axis) designed for NeuralNetwork Playground of Deep Insider.
|
|
140
|
+
ax = get_playground_axes(fig)
|
|
141
|
+
|
|
142
|
+
plot_points(ax, X_train, y_train, X_valid, y_valid)
|
|
143
|
+
|
|
144
|
+
return fig, ax
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def _add_colorbar_on_bottom(fig, ax, im):
|
|
148
|
+
divider = make_axes_locatable(ax)
|
|
149
|
+
ax_cb = divider.append_axes("bottom", size="5%", pad=0.3)
|
|
150
|
+
ax_cb.tick_params(axis='x', colors='#777777')
|
|
151
|
+
ax_cb.tick_params(axis='y', colors='#777777')
|
|
152
|
+
cb = fig.colorbar(im, cax=ax_cb, ticks=TICKS_VALUE, orientation='horizontal')
|
|
153
|
+
cb.outline.set_edgecolor('white')
|
|
154
|
+
cb.outline.set_linewidth(0)
|
|
155
|
+
cb.solids.set_edgecolor("face")
|
|
156
|
+
ax_cb.xaxis.set_ticks_position("bottom")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def plot_sample(data_type, noise=0.0, validation_size=0.5, visualize_validation_data=False, figsize=(5, 5), dpi=100, node_id=None, discretize=False, training_size=-1.0):
|
|
160
|
+
|
|
161
|
+
data_array = generate_data(data_type, noise)
|
|
162
|
+
if data_array is None:
|
|
163
|
+
return None, None
|
|
164
|
+
|
|
165
|
+
mat = np.array(data_array)
|
|
166
|
+
X_train, y_train, X_valid, y_valid = split_data(mat, validation_size=validation_size, training_size=training_size)
|
|
167
|
+
|
|
168
|
+
fig, ax = plot_points_with_playground_style(X_train, y_train, X_valid if (visualize_validation_data) else None, y_valid if (visualize_validation_data) else None, figsize=figsize, dpi=dpi)
|
|
169
|
+
|
|
170
|
+
if node_id is not None:
|
|
171
|
+
draw_decision_boundary(fig, ax, node_id=node_id, discretize=discretize)
|
|
172
|
+
|
|
173
|
+
return fig, ax
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def predict_classes(trained_model, x, batch_size=32, verbose=0):
|
|
177
|
+
"""Generate class predictions for the input samples.
|
|
178
|
+
The input samples are processed batch by batch.
|
|
179
|
+
# Arguments
|
|
180
|
+
x: input data, as a Numpy array or list of Numpy arrays
|
|
181
|
+
(if the trained_model has multiple inputs).
|
|
182
|
+
batch_size: integer.
|
|
183
|
+
verbose: verbosity mode, 0 or 1.
|
|
184
|
+
# Returns:
|
|
185
|
+
A numpy array of class predictions.
|
|
186
|
+
"""
|
|
187
|
+
preds = trained_model.predict(x, batch_size=batch_size, verbose=verbose)
|
|
188
|
+
discretized = np.where(preds >= 0.0, 1, -1)
|
|
189
|
+
return discretized
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def predict_proba(trained_model, x, batch_size=32, verbose=0):
|
|
193
|
+
"""Generates class probability predictions for the input samples.
|
|
194
|
+
The input samples are processed batch by batch.
|
|
195
|
+
# Arguments
|
|
196
|
+
x: input data, as a Numpy array or list of Numpy arrays
|
|
197
|
+
(if the trained_model has multiple inputs).
|
|
198
|
+
batch_size: integer.
|
|
199
|
+
verbose: verbosity mode, 0 or 1.
|
|
200
|
+
# Returns
|
|
201
|
+
A Numpy array of probability predictions.
|
|
202
|
+
"""
|
|
203
|
+
probability = trained_model.predict(x, batch_size=batch_size, verbose=verbose)
|
|
204
|
+
return probability
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def predict_classes_proba(trained_model, x, batch_size=32, verbose=0):
|
|
208
|
+
"""Generate class and probability predictions for the input samples.
|
|
209
|
+
The input samples are processed batch by batch.
|
|
210
|
+
# Arguments
|
|
211
|
+
x: input data, as a Numpy array or list of Numpy arrays
|
|
212
|
+
(if the trained_model has multiple inputs).
|
|
213
|
+
batch_size: integer.
|
|
214
|
+
verbose: verbosity mode, 0 or 1.
|
|
215
|
+
# Returns:
|
|
216
|
+
A numpy array of class and probability predictions.
|
|
217
|
+
"""
|
|
218
|
+
probability = trained_model.predict(x, batch_size=batch_size, verbose=verbose)
|
|
219
|
+
discretized = np.where(probability >= 0.0, 1, -1)
|
|
220
|
+
return discretized, probability
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def draw_decision_boundary(fig, ax, node_id=InputType.X1, trained_model=None, discretize=False, enable_colorbar=True):
|
|
224
|
+
|
|
225
|
+
if trained_model is None:
|
|
226
|
+
im = HeatMap.updateBackground(ax, None, node_id, discretize)
|
|
227
|
+
else:
|
|
228
|
+
boundary_array = Player.get_boundary_array()
|
|
229
|
+
probability = predict_proba(trained_model, boundary_array)
|
|
230
|
+
im = HeatMap.updateBackground(ax, None, None, discretize, probability)
|
|
231
|
+
|
|
232
|
+
if enable_colorbar:
|
|
233
|
+
_add_colorbar_on_bottom(fig, ax, im)
|
|
234
|
+
|
|
235
|
+
return im
|
|
236
|
+
|
plygdata/dataset.py
ADDED
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# Copyright 2018-2019 Digital Advantage Co., Ltd. All Rights Reserved.
|
|
3
|
+
#
|
|
4
|
+
# This is a Python implementation of [tensorflow / playground (Deep playground) / dataset.ts](https://github.com/tensorflow/playground/blob/master/src/dataset.ts).
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
# ==============================================================================
|
|
18
|
+
|
|
19
|
+
from __future__ import division
|
|
20
|
+
|
|
21
|
+
import random
|
|
22
|
+
import math
|
|
23
|
+
from plygdata.scalelinear import ScaleLinear
|
|
24
|
+
from plygdata.state import DatasetType
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
NUM_SAMPLES_CLASSIFY = 500
|
|
28
|
+
NUM_SAMPLES_REGRESS = 1200
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
#class Example2D:
|
|
32
|
+
# """
|
|
33
|
+
# A two dimensional example: x and y coordinates with the label.
|
|
34
|
+
# """
|
|
35
|
+
# def __init__(self, x=0, y=0, label=0) :
|
|
36
|
+
# self.x = x
|
|
37
|
+
# self.y = y
|
|
38
|
+
# self.label = label
|
|
39
|
+
|
|
40
|
+
class Point:
|
|
41
|
+
def __init__(self, x = 0.0, y = 0.0):
|
|
42
|
+
self.x = x
|
|
43
|
+
self.y = y
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def rand_uniform(a, b):
|
|
47
|
+
"""
|
|
48
|
+
Returns a sample from a uniform [a, b] distribution.
|
|
49
|
+
"""
|
|
50
|
+
return random.random() * (b - a) + a
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def normal_random(mean = 0.0, variance = 1.0):
|
|
54
|
+
"""
|
|
55
|
+
Samples from a normal distribution.
|
|
56
|
+
|
|
57
|
+
:param mean
|
|
58
|
+
The mean. Default is 0.0.
|
|
59
|
+
:param variance
|
|
60
|
+
The variance. Default is 1.0.
|
|
61
|
+
"""
|
|
62
|
+
while True:
|
|
63
|
+
v1 = 2.0 * random.random() - 1.0
|
|
64
|
+
v2 = 2.0 * random.random() - 1.0
|
|
65
|
+
s = v1 * v1 + v2 * v2
|
|
66
|
+
if 0.0 < s <= 1.0:
|
|
67
|
+
break
|
|
68
|
+
result = math.sqrt(-2.0 * math.log(s) / s) * v1
|
|
69
|
+
return mean + math.sqrt(variance) * result
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def dist(a, b):
|
|
73
|
+
"""
|
|
74
|
+
Returns the euclidean distance between two points in space.
|
|
75
|
+
"""
|
|
76
|
+
dx = a.x - b.x
|
|
77
|
+
dy = a.y - b.y
|
|
78
|
+
return math.sqrt(dx * dx + dy * dy)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def shuffle(array):
|
|
82
|
+
"""
|
|
83
|
+
Shuffles the array using Fisher-Yates algorithm. Uses the seedrandom
|
|
84
|
+
library as the random generator.
|
|
85
|
+
"""
|
|
86
|
+
counter = len(array)
|
|
87
|
+
temp = 0
|
|
88
|
+
index = 0
|
|
89
|
+
# While there are elements in the array
|
|
90
|
+
while counter > 0:
|
|
91
|
+
# Pick a random index
|
|
92
|
+
index = int(math.floor(random.random() * counter))
|
|
93
|
+
# Decrease counter by 1
|
|
94
|
+
counter -= 1
|
|
95
|
+
# And swap the last element with it
|
|
96
|
+
temp = array[counter]
|
|
97
|
+
array[counter] = array[index]
|
|
98
|
+
array[index] = temp
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def generate_data(data_type, noise = 0.0):
|
|
102
|
+
|
|
103
|
+
if data_type == DatasetType.ClassifyTwoGaussData:
|
|
104
|
+
data_array = DataGenerator.classify_two_gauss(NUM_SAMPLES_CLASSIFY, noise=noise)
|
|
105
|
+
elif data_type == DatasetType.ClassifyXORData:
|
|
106
|
+
data_array = DataGenerator.classify_xor(NUM_SAMPLES_CLASSIFY, noise=noise)
|
|
107
|
+
elif data_type == DatasetType.ClassifyCircleData:
|
|
108
|
+
data_array = DataGenerator.classify_circle(NUM_SAMPLES_CLASSIFY, noise=noise)
|
|
109
|
+
elif data_type == DatasetType.ClassifySpiralData:
|
|
110
|
+
data_array = DataGenerator.classify_spiral(NUM_SAMPLES_CLASSIFY, noise=noise)
|
|
111
|
+
elif data_type == DatasetType.RegressPlane:
|
|
112
|
+
data_array = DataGenerator.regress_plane(NUM_SAMPLES_REGRESS, noise=noise)
|
|
113
|
+
elif data_type == DatasetType.RegressGaussian:
|
|
114
|
+
data_array = DataGenerator.regress_gaussian(NUM_SAMPLES_REGRESS, noise=noise)
|
|
115
|
+
else:
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
return data_array
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class DataGenerator:
|
|
122
|
+
|
|
123
|
+
@staticmethod
|
|
124
|
+
def classify_two_gauss(numSamples, noise = 0.0):
|
|
125
|
+
points = []
|
|
126
|
+
|
|
127
|
+
varianceScale = ScaleLinear(domain=[0.0, 0.5], slrange=[0.5, 4.0])
|
|
128
|
+
variance = varianceScale(noise)
|
|
129
|
+
|
|
130
|
+
def genGauss(cx, cy, label):
|
|
131
|
+
for _ in range(numSamples // 2):
|
|
132
|
+
x = normal_random(cx, variance)
|
|
133
|
+
y = normal_random(cy, variance)
|
|
134
|
+
points.append([x, y, label])
|
|
135
|
+
|
|
136
|
+
genGauss(2.0, 2.0, 1) # Gaussian with positive examples.
|
|
137
|
+
genGauss(-2.0, -2.0, -1) # Gaussian with negative examples.
|
|
138
|
+
return points
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@staticmethod
|
|
142
|
+
def classify_xor(numSamples, noise = 0.0):
|
|
143
|
+
|
|
144
|
+
def getXORLabel(p):
|
|
145
|
+
return 1 if (p.x * p.y >= 0) else -1
|
|
146
|
+
|
|
147
|
+
points = []
|
|
148
|
+
for _ in range(numSamples):
|
|
149
|
+
x = rand_uniform(-5.0, 5.0)
|
|
150
|
+
padding = 0.3
|
|
151
|
+
x = x + padding if (x > 0) else x - padding # Padding.
|
|
152
|
+
y = rand_uniform(-5.0, 5.0)
|
|
153
|
+
y = y + padding if (y > 0) else y - padding
|
|
154
|
+
noiseX = rand_uniform(-5.0, 5.0) * noise
|
|
155
|
+
noiseY = rand_uniform(-5.0, 5.0) * noise
|
|
156
|
+
label = getXORLabel(Point(x + noiseX, y + noiseY))
|
|
157
|
+
points.append([x, y, label])
|
|
158
|
+
return points
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@staticmethod
|
|
162
|
+
def classify_circle(numSamples, noise = 0.0):
|
|
163
|
+
points = []
|
|
164
|
+
radius = 5.0
|
|
165
|
+
|
|
166
|
+
def getCircleLabel(p, center):
|
|
167
|
+
return 1 if (dist(p, center) < (radius * 0.5)) else -1
|
|
168
|
+
|
|
169
|
+
# Generate positive points inside the circle.
|
|
170
|
+
for _ in range(numSamples // 2):
|
|
171
|
+
r = rand_uniform(0.0, radius * 0.5)
|
|
172
|
+
angle = rand_uniform(0.0, 2.0 * math.pi)
|
|
173
|
+
x = r * math.sin(angle)
|
|
174
|
+
y = r * math.cos(angle)
|
|
175
|
+
noiseX = rand_uniform(-radius, radius) * noise
|
|
176
|
+
noiseY = rand_uniform(-radius, radius) * noise
|
|
177
|
+
label = getCircleLabel(Point(x + noiseX, y + noiseY), Point(0.0, 0.0))
|
|
178
|
+
points.append([x, y, label])
|
|
179
|
+
|
|
180
|
+
# Generate negative points outside the circle.
|
|
181
|
+
for _ in range(numSamples // 2):
|
|
182
|
+
r = rand_uniform(radius * 0.7, radius)
|
|
183
|
+
angle = rand_uniform(0.0, 2.0 * math.pi)
|
|
184
|
+
x = r * math.sin(angle)
|
|
185
|
+
y = r * math.cos(angle)
|
|
186
|
+
noiseX = rand_uniform(-radius, radius) * noise
|
|
187
|
+
noiseY = rand_uniform(-radius, radius) * noise
|
|
188
|
+
label = getCircleLabel(Point(x + noiseX, y + noiseY), Point(0.0, 0.0))
|
|
189
|
+
points.append([x, y, label])
|
|
190
|
+
|
|
191
|
+
return points
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@staticmethod
|
|
195
|
+
def classify_spiral(numSamples, noise = 0.0):
|
|
196
|
+
points = []
|
|
197
|
+
n = numSamples // 2
|
|
198
|
+
|
|
199
|
+
def genSpiral(deltaT, label):
|
|
200
|
+
for i in range(n):
|
|
201
|
+
r = i / n * 5
|
|
202
|
+
t = 1.75 * i / n * 2 * math.pi + deltaT
|
|
203
|
+
x = r * math.sin(t) + rand_uniform(-1.0, 1.0) * noise
|
|
204
|
+
y = r * math.cos(t) + rand_uniform(-1.0, 1.0) * noise
|
|
205
|
+
points.append([x, y, label])
|
|
206
|
+
|
|
207
|
+
genSpiral(0.0, 1) # Positive examples.
|
|
208
|
+
genSpiral(math.pi, -1) # Negative examples.
|
|
209
|
+
|
|
210
|
+
return points
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
@staticmethod
|
|
214
|
+
def regress_plane(numSamples, noise = 0.0):
|
|
215
|
+
radius = 6
|
|
216
|
+
labelScale = ScaleLinear(domain=[-10, 10], slrange=[-1, 1])
|
|
217
|
+
getLabel = lambda x, y: labelScale(x + y)
|
|
218
|
+
|
|
219
|
+
points = []
|
|
220
|
+
for _ in range(numSamples):
|
|
221
|
+
x = rand_uniform(-radius, radius)
|
|
222
|
+
y = rand_uniform(-radius, radius)
|
|
223
|
+
noiseX = rand_uniform(-radius, radius) * noise
|
|
224
|
+
noiseY = rand_uniform(-radius, radius) * noise
|
|
225
|
+
label = getLabel(x + noiseX, y + noiseY)
|
|
226
|
+
points.append([x, y, label])
|
|
227
|
+
|
|
228
|
+
return points
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
@staticmethod
|
|
232
|
+
def regress_gaussian(numSamples, noise = 0.0):
|
|
233
|
+
points = []
|
|
234
|
+
|
|
235
|
+
labelScale = ScaleLinear(domain=[0.0, 2.0], slrange=[1.0, 0.0], clamp=True)
|
|
236
|
+
|
|
237
|
+
gaussians = [
|
|
238
|
+
[-4.0, 2.5, 1.0],
|
|
239
|
+
[ 0.0, 2.5, -1.0],
|
|
240
|
+
[ 4.0, 2.5, 1.0],
|
|
241
|
+
[-4.0, -2.5, -1.0],
|
|
242
|
+
[ 0.0, -2.5, 1.0],
|
|
243
|
+
[ 4.0, -2.5, -1.0]
|
|
244
|
+
]
|
|
245
|
+
|
|
246
|
+
def getLabel(x, y):
|
|
247
|
+
# Choose the one that is maximum in abs value.
|
|
248
|
+
curlabel = 0
|
|
249
|
+
for c in gaussians:
|
|
250
|
+
cx = c[0]
|
|
251
|
+
cy = c[1]
|
|
252
|
+
sign = c[2]
|
|
253
|
+
newLabel = sign * labelScale(dist(Point(x, y), Point(cx, cy)))
|
|
254
|
+
if abs(newLabel) > abs(curlabel):
|
|
255
|
+
curlabel = newLabel
|
|
256
|
+
return curlabel
|
|
257
|
+
|
|
258
|
+
radius = 6.0
|
|
259
|
+
for _ in range(numSamples):
|
|
260
|
+
x = rand_uniform(-radius, radius)
|
|
261
|
+
y = rand_uniform(-radius, radius)
|
|
262
|
+
noiseX = rand_uniform(-radius, radius) * noise
|
|
263
|
+
noiseY = rand_uniform(-radius, radius) * noise
|
|
264
|
+
label = getLabel(x + noiseX, y + noiseY)
|
|
265
|
+
points.append([x, y, label])
|
|
266
|
+
|
|
267
|
+
return points
|
|
268
|
+
|
plygdata/heatmap.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# ==============================================================================
|
|
2
|
+
# Copyright 2018-2019 Digital Advantage Co., Ltd. All Rights Reserved.
|
|
3
|
+
#
|
|
4
|
+
# This is a Python implementation of [tensorflow / playground (Deep playground) / heatmap.ts](https://github.com/tensorflow/playground/blob/master/src/heatmap.ts).
|
|
5
|
+
#
|
|
6
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
7
|
+
# you may not use this file except in compliance with the License.
|
|
8
|
+
# You may obtain a copy of the License at
|
|
9
|
+
#
|
|
10
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
11
|
+
#
|
|
12
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
13
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
14
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
15
|
+
# See the License for the specific language governing permissions and
|
|
16
|
+
# limitations under the License.
|
|
17
|
+
# ==============================================================================
|
|
18
|
+
|
|
19
|
+
from __future__ import division
|
|
20
|
+
|
|
21
|
+
from plygdata.datacolor import DataColor
|
|
22
|
+
from plygdata.playground import Player
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
RECT_DOMAIN = [-6.0, 6.0, -6.0, 6.0]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class HeatMap:
|
|
29
|
+
'''
|
|
30
|
+
Draws a heatmap using matplotlib.
|
|
31
|
+
Used for showing the learned decision boundary of the classification algorithm.
|
|
32
|
+
Can also draw data points on top of the heatmap figure using matplotlib.
|
|
33
|
+
'''
|
|
34
|
+
|
|
35
|
+
@staticmethod
|
|
36
|
+
def draw_decision_boundary_of_node(ax, boundary_of_node):
|
|
37
|
+
cmap, norm = DataColor.get_colormap()
|
|
38
|
+
alpha = (160 / 255)
|
|
39
|
+
im = ax.imshow(boundary_of_node, extent=RECT_DOMAIN, cmap=cmap, norm=norm, alpha=alpha, interpolation='bicubic')
|
|
40
|
+
return im
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def updateBackground(ax, boundaries, node_id, discretize, probability=None):
|
|
45
|
+
if probability is None:
|
|
46
|
+
boundaries = Player.update_decision_boundary(boundaries, discretize)
|
|
47
|
+
boundary_of_node = boundaries[node_id]
|
|
48
|
+
else:
|
|
49
|
+
boundary_of_node = Player.get_decision_boundary_of_node(probability, discretize)
|
|
50
|
+
im = HeatMap.draw_decision_boundary_of_node(ax, boundary_of_node)
|
|
51
|
+
return im
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
@staticmethod
|
|
55
|
+
def updateCircles(ax, X_points, y_label, edgecolors):
|
|
56
|
+
for i in range(len(X_points)):
|
|
57
|
+
ax.scatter(X_points[i, 0], X_points[i, 1], s=20, c=DataColor.get_rgb(y_label[i].item()), alpha=0.9, linewidths=0.7, edgecolors=edgecolors)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@staticmethod
|
|
61
|
+
def updateTrainPoints(ax, X_train, y_train):
|
|
62
|
+
HeatMap.updateCircles(ax, X_train, y_train, "#ffffff")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
@staticmethod
|
|
66
|
+
def updateValidationPoints(ax, X_valid, y_valid):
|
|
67
|
+
HeatMap.updateCircles(ax, X_valid, y_valid, "#555555")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
def updateTestPoints(ax, X_valid, y_valid):
|
|
72
|
+
HeatMap.updateCircles(ax, X_valid, y_valid, "#FF5555")
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
@staticmethod
|
|
76
|
+
def reduceMatrix(matrix, factor):
|
|
77
|
+
|
|
78
|
+
len_matrix = len(matrix)
|
|
79
|
+
if not isinstance(factor, int) or isinstance(factor, bool) or factor <= 0:
|
|
80
|
+
raise ValueError("The reduction factor must be a positive integer")
|
|
81
|
+
if not len_matrix or any(len(row) != len_matrix for row in matrix):
|
|
82
|
+
raise ValueError("The provided matrix must be a nonempty square matrix")
|
|
83
|
+
if len_matrix % factor:
|
|
84
|
+
raise ValueError("The width/height of the matrix must be divisible by the reduction factor")
|
|
85
|
+
return [
|
|
86
|
+
[sum(matrix[i + k][j + l] for k in range(factor) for l in range(factor))
|
|
87
|
+
/ (factor * factor) for j in range(0, len_matrix, factor)]
|
|
88
|
+
for i in range(0, len_matrix, factor)
|
|
89
|
+
]
|