blob: 7028b6eadf882ca9544f5545f541d33e3896214f [file] [log] [blame] [edit]
# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests SoftmaxRegression class.
Generates some fake data and tries to overfit the data with SoftmaxRegression.
"""
import unittest
from edgetpu.learn.backprop.softmax_regression import SoftmaxRegression
import numpy as np
def generate_fake_data(class_sizes, means, cov_mats):
"""Generates fake data for training and testing.
Examples from same class is drawn from the same MultiVariate Normal (MVN)
distribution.
# classes = len(class_sizes) = len(means) = len(cov_mats)
dim of MVN = cov_mats[0].shape[0]
Args:
class_sizes: list of ints, number of examples to draw from each class.
means: list of list of floats, mean value of each MVN distribution.
cov_mats: list of ndarray, each element is a k by k ndarray, which
represents the covariance matrix in MVN distribution, k is the dimension
of MVN distribution.
Returns:
a tuple of data and labels. data and labels are shuffled.
"""
# Some sanity checks
assert len(class_sizes) == len(means)
assert len(class_sizes) == len(cov_mats)
num_data = np.sum(class_sizes)
feature_dim = len(means[0])
data = np.empty((num_data, feature_dim))
labels = np.empty((num_data), dtype=int)
start_idx = 0
class_idx = 0
for size, mean, cov_mat in zip(class_sizes, means, cov_mats):
data[start_idx:start_idx + size] = np.random.multivariate_normal(
mean, cov_mat, size)
labels[start_idx:start_idx + size] = np.ones(size, dtype=int) * class_idx
start_idx += size
class_idx += 1
perm = np.random.permutation(data.shape[0])
data = data[perm, :]
labels = labels[perm]
return data, labels
class SoftmaxRegressionTest(unittest.TestCase):
def testSoftmaxRegressionLinearSeparableData(self):
# Fake data is generated from 3 MVN distributions, these MVN distributionss
# are tuned to be well-separated, such that it can be separated by
# SoftmaxRegression model (which is a linear classifier).
num_train = 200
num_val = 30
# Let's distribute data evenly among different classes.
num_classes = 3
class_sizes = ((num_train + num_val) // num_classes) * np.ones(
num_classes, dtype=int)
class_sizes[-1] = (num_train + num_val) - np.sum(class_sizes[0:-1])
# 3 is chosen, such that each pair of mean is over 6 `sigma` distance
# apart. Which makes classes harder to `touch` each other.
# https://en.wikipedia.org/wiki/68%E2%80%9395%E2%80%9399.7_rule
means = np.array([[1, 1], [-1, -1], [1, -1]]) * 3
feature_dim = len(means[0])
cov_mats = [np.eye(feature_dim)] * num_classes
model = SoftmaxRegression(feature_dim, num_classes)
np.random.seed(12345)
all_data, all_labels = generate_fake_data(class_sizes, means, cov_mats)
dataset = {}
dataset['data_train'] = all_data[0:num_train]
dataset['labels_train'] = all_labels[0:num_train]
dataset['data_val'] = all_data[num_train:]
dataset['labels_val'] = all_labels[num_train:]
# Train with SGD.
num_iter = 20
learning_rate = 0.01
model.train_with_sgd(
dataset, num_iter, learning_rate, batch_size=100, print_every=5)
self.assertGreater(
model.get_accuracy(dataset['data_train'], dataset['labels_train']),
0.99)
def testSoftmaxRegressionLinearNonSeparableData(self):
# Fake data is generated from 3 MVN distributions, these MVN distributions
# are NOT well-separated.
num_train = 200
num_val = 30
# Let's distribute data evenly among different classes.
num_classes = 3
class_sizes = ((num_train + num_val) // num_classes) * np.ones(
num_classes, dtype=int)
class_sizes[-1] = (num_train + num_val) - np.sum(class_sizes[0:-1])
means = np.array([[1, 1], [-1, -1], [1, -1]])
feature_dim = len(means[0])
cov_mats = [np.eye(feature_dim)] * num_classes
model = SoftmaxRegression(feature_dim, num_classes)
np.random.seed(54321)
all_data, all_labels = generate_fake_data(class_sizes, means, cov_mats)
dataset = {}
dataset['data_train'] = all_data[0:num_train]
dataset['labels_train'] = all_labels[0:num_train]
dataset['data_val'] = all_data[num_train:]
dataset['labels_val'] = all_labels[num_train:]
# Train with SGD.
num_iter = 50
learning_rate = 0.1
model.train_with_sgd(
dataset, num_iter, learning_rate, batch_size=100, print_every=5)
self.assertGreater(
model.get_accuracy(dataset['data_train'], dataset['labels_train']), 0.8)