Thursday, April 26, 2018

Dimensionality Reduction and Scattered Data Visualization with MNIST

We live in 3D world, and we can only view scattered data in 1D, 2D, or 3D. Yet, we deal with data that have very large dimension. Consider MNIST dataset, which is considered to be a toy example in deep learning field, consists of 28 X 28 gray images; that is 784 dimensions.

How would this MNIST data look like in 2D or 3D after dimensionality reduction? Let's figure it out! I am going to write the code in Pytorch. I have to say, Pytorch is so much better than other deep learning libraries, such as Theano or Tensorflow. Of course, it is just my personal opinion, so let's not get into this argument here.

What I want to do is to take Pytorch's MNIST example found here, and make some modifications to reduce the data dimension to 2D and plot scattered data. This will be a very good example that shows how to do all the following in Pytorch:
1. Create a custom network
2. Create a custom layer
3. Transfer learning from an existing model
4. Save and load a model

Here is the plot I get from running the code below.


This code is tested on Pytorch 0.3.1.
import math
import os
import numpy as np
import matplotlib.pyplot as plt
import argparse
from torch.nn.parameter import Parameter
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from torch.autograd import Variable
# Training settings
parser = argparse.ArgumentParser(description='PyTorch MNIST Example')
parser.add_argument('--batch-size', type=int, default=64, metavar='N',
help='input batch size for training (default: 64)')
parser.add_argument('--test-batch-size', type=int, default=1000, metavar='N',
help='input batch size for testing (default: 1000)')
parser.add_argument('--epochs', type=int, default=10, metavar='N',
help='number of epochs to train (default: 10)')
parser.add_argument('--lr', type=float, default=0.01, metavar='LR',
help='learning rate (default: 0.01)')
parser.add_argument('--momentum', type=float, default=0.5, metavar='M',
help='SGD momentum (default: 0.5)')
parser.add_argument('--no-cuda', action='store_true', default=False,
help='disables CUDA training')
parser.add_argument('--seed', type=int, default=1, metavar='S',
help='random seed (default: 1)')
parser.add_argument('--log-interval', type=int, default=10, metavar='N',
help='how many batches to wait before logging training status')
args = parser.parse_args()
args.cuda = not args.no_cuda and torch.cuda.is_available()
torch.manual_seed(args.seed)
if args.cuda:
torch.cuda.manual_seed(args.seed)
kwargs = {'num_workers': 1, 'pin_memory': True} if args.cuda else {}
train_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data', train=True, download=True,
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])),
batch_size=args.batch_size, shuffle=True, **kwargs)
test_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data', train=False, transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])),
batch_size=args.test_batch_size, shuffle=True, **kwargs)
class Net(nn.Module):
def __init__(self, ndim=2, last_layer=True):
super(Net, self).__init__()
self.last_layer = last_layer
self.ndim = ndim
self.conv1 = nn.Conv2d(1, 10, kernel_size=3)
self.bn1 = nn.BatchNorm2d(10, affine=True)
self.conv2 = nn.Conv2d(10, 10, kernel_size=3)
self.conv3 = nn.Conv2d(10, 20, kernel_size=3)
self.bn2 = nn.BatchNorm2d(20, affine=True)
self.conv4 = nn.Conv2d(20, 20, kernel_size=3)
self.conv4_drop = nn.Dropout2d()
self.fc1 = nn.Linear(320, 50)
self.fc2= nn.Linear(50, ndim)
self.softmax = AngleSoftmax(10)
def forward(self, x):
x = F.elu(self.conv1(x))
x = self.bn1(x)
x = F.elu(F.max_pool2d(self.conv2(x), 2))
x = F.elu(self.conv3(x))
x = self.bn2(x)
x = F.elu(F.max_pool2d(self.conv4_drop(self.conv4(x)), 2))
x = x.view(-1, 320)
x = F.elu(self.fc1(x))
x = self.fc2(x)
if self.last_layer is True:
x = self.softmax(x)
return x
class AngleSoftmax(nn.Module):
def __init__(self, out_feature):
super(AngleSoftmax, self).__init__()
self.out_feature = out_feature
self.weight = Parameter(torch.Tensor(1, out_feature))
self.reset_parameters()
def reset_parameters(self):
stdv = 1. / math.sqrt(self.weight.size(1))
self.weight.data.uniform_(-stdv, stdv)
def forward(self, x):
u = torch.cos(self.weight)
v = torch.sin(self.weight)
w = torch.cat([u, v], dim=0)
x = x.mm(w) / torch.sum(x**2, dim=1, keepdim=True) ** 0.5
return F.log_softmax(x)
def train(epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
if args.cuda:
data, target = data.cuda(), target.cuda()
data, target = Variable(data), Variable(target)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % args.log_interval == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.data[0]))
def test():
model.eval()
test_loss = 0
correct = 0
for data, target in test_loader:
if args.cuda:
data, target = data.cuda(), target.cuda()
data, target = Variable(data, volatile=True), Variable(target)
output = model(data)
test_loss += F.nll_loss(output, target, size_average=False).data[0] # sum up batch loss
pred = output.data.max(1, keepdim=True)[1] # get the index of the max log-probability
correct += pred.eq(target.data.view_as(pred)).long().cpu().sum()
test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
def plot_features(model):
model.eval()
for data, target in test_loader:
if args.cuda:
data, target = data.cuda(), target.cuda()
data, target = Variable(data, volatile=True), Variable(target)
output = model(data)
output = output.cpu().data.numpy()
target = target.cpu().data.numpy()
for label in range(10):
idx = target == label
plt.scatter(output[idx,0], output[idx,1])
plt.legend(np.arange(10, dtype=np.int32))
plt.show()
break
if __name__ == '__main__':
model = Net(2)
if args.cuda:
model.cuda()
optimizer = optim.SGD(model.parameters(), lr=args.lr, momentum=args.momentum)
model_file = 'model.h5'
if not os.path.isfile(model_file):
for epoch in range(1, args.epochs + 1):
train(epoch)
test()
torch.save(model, model_file)
else:
model = torch.load(model_file)
feature_model = Net(2, last_layer=False)
feature_model.load_state_dict(model.state_dict())
if args.cuda:
feature_model.cuda()
plot_features(feature_model)
view raw mnist.py hosted with ❤ by GitHub

Wednesday, April 25, 2018

Three Minutes Daily Vim Tip: Highlight Current Cursor Line

In Vim, it is often difficult to locate the current cursor, especially when you jump around. Here is a tip to easily locate the cursor.

Simply add the following line to ~/.vimrc
set cursorline

That's it!

Python IDE with YouCompleteMe

OK, I have tried YouCompleteMe, but then I stopped using it after some time. Well, I am going to give it a try once more. The reason is that I am usually developing on a server through ssh. I could launch GUI-based IDE, such as PyCharm through X-forwarding, but the response is too slow. I realized that editing with Vim on a remote server is probably the best option here.

In any case, I explained how to install YouCompleteMe plugin in my previous post. Here, I will talk about a few more useful tips.

One of the must-have feature in IDE is perhaps GoTo feature, where one can go to the definition of certain method of variable. YouCompleteMe also provides this feature. Formally, this is mapped with
:YcmCompleter GoTo

We can create a shortcut. Add the following in the ~/.vimrc file
nnoremap <C-P> :YcmCompleter GoTo<CR>

In the source file that YouCompleteMe understands, you can simply move the cursor to the symbol and press <Control> + p key to go to the definition. To go back, you can press <Control> + o key.

Also, add the following to ~/.vimrc file
let g:ycm_python_binary_path = 'python'

This will load the default python first found on PATH environment.

Happy coding!

Tuesday, April 17, 2018

Android Camera2 Bare Minimum Code Sample

Google has replaced camera API with camera2 API a few years ago. I guess it is a good thing that Google is working hard to improve Android, personally I think camera2 API is just so difficult. Compared to the deprecated camera API, it requires so much more code.

I am trying to understand this new camera2 API, and it is just not easy. I looked at Google's Camera2Basic sample code, it is still daunting for Android beginners like myself. Well, here is my attempt to trim down this fat sample code into the bare minimum so that it is easier to understand the fundamentals of the camera2 API.

All it does is to connect to a camera and display its preview to the screen. No capture, no manual focus, no error checks, etc. I am not happy that even this bare-bone app requires so many lines. Anyways, I will add more functionalities in the upcoming post.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.unixnme.camera2.MainActivity">
<TextureView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/preview"/>
</android.support.constraint.ConstraintLayout>
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unixnme.camera2">
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.level.full" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
package com.unixnme.camera2;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.util.Size;
import android.view.Surface;
import android.view.TextureView;
import java.util.Arrays;
import java.util.Collections;
public class MainActivity extends AppCompatActivity {
private TextureView mTextureView;
final private static String TAG = MainActivity.class.getSimpleName();
// preview related ////////////////////////////////////////////////////////////////
private CaptureRequest.Builder mCaptureRequestbuilder;
private final TextureView.SurfaceTextureListener mSurfaceTextureListener
= new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
openCamera(width, height);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture texture) {
}
};
final private CameraCaptureSession.StateCallback mCaptureStateCallback = new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
try {
cameraCaptureSession.setRepeatingRequest(mCaptureRequestbuilder.build(), null, null);
} catch (Throwable t) {
handleException(t);
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
}
};
private void createCameraPreviewSession() {
SurfaceTexture texture = mTextureView.getSurfaceTexture();
texture.setDefaultBufferSize(mSize.getWidth(), mSize.getHeight());
Surface surface = new Surface(texture);
try {
mCaptureRequestbuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
mCaptureRequestbuilder.addTarget(surface);
mCameraDevice.createCaptureSession(Collections.singletonList(surface), mCaptureStateCallback, null);
} catch (Throwable t) {
handleException(t);
}
}
// camera related /////////////////////////////////////////////////////////////
private CameraDevice mCameraDevice;
private Size mSize;
final private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice cameraDevice) {
mCameraDevice = cameraDevice;
createCameraPreviewSession();
}
@Override
public void onDisconnected(@NonNull CameraDevice cameraDevice) {
mCameraDevice = null;
}
@Override
public void onError(@NonNull CameraDevice cameraDevice, int i) {
mCameraDevice = null;
}
};
private void openCamera(int width, int height) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
requestCameraPermission();
return;
}
CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
String cameraId = cameraManager.getCameraIdList()[0];
CameraCharacteristics characteristics
= cameraManager.getCameraCharacteristics(cameraId);
StreamConfigurationMap map = characteristics.get(
CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
mSize = map.getOutputSizes(SurfaceTexture.class)[0];
cameraManager.openCamera(cameraId, mStateCallback, null);
} catch (Throwable t) {
handleException(t);
}
}
private void closeCamera() {
try {
if (null != mCameraDevice)
mCameraDevice.close();
} catch (Throwable t) {
handleException(t);
}
}
// activity lifecycle related /////////////////////////////////////////////////
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextureView = findViewById(R.id.preview);
}
@Override
protected void onResume() {
super.onResume();
// we open up camera after the textureview is available
// either way, we will call openCamera
if (mTextureView.isAvailable()) {
// call now
int width = mTextureView.getWidth();
int height = mTextureView.getHeight();
openCamera(width, height);
} else {
// call from the listener callback
mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
}
}
@Override
public void onPause() {
super.onPause();
closeCamera();
}
// request related ///////////////////////////////////////////////////////////////
private static final int REQUEST_CAMERA_PERMISSION = 1;
private void requestCameraPermission() {
requestPermissions(new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
if (requestCode == REQUEST_CAMERA_PERMISSION) {
if (grantResults.length != 1 || grantResults[0] != PackageManager.PERMISSION_GRANTED) {
// permissio not granted; quit the app
this.finish();
}
} else {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
// Exceptions ////////////////////////////////////////////////////////////////////////
private void handleException(Throwable t) {
Log.d(TAG, t.getMessage());
t.printStackTrace();
}
}


Sunday, April 15, 2018

OpenCV on PyCharm

PyCharm is such a nice IDE for developing in Python. I really love it. However, with OpenCV module, I realized that PyCharm didn't recognize cv2 module.

After some search, here is a very simple solution! Credit goes to here.
When you import cv2, simply copy paste the whole thing below:

import cv2

# this is just to unconfuse pycharm
try:
    from cv2 import cv2
except ImportError:
    pass

That's it!

Friday, April 13, 2018

Redirecting Outputs from Parallel Subprocess Runs

Say you are call some executable from python in parallel. You don't want to clog the stdout, so you want to save each stdout from executable as a file. Below is what you can do.

import subprocess
import multiprocessing as mp
def run(cmd):
with open(cmd[-1], 'w') as f:
return subprocess.run(cmd[:-1], stdout=f)
numbers = []
i = 100000000
for _ in range(10):
numbers.append(i)
i //= 2
commands = [['./a.out', str(x), str(x) + '.txt'] for x in numbers]
nproc = 2
pool = mp.Pool(nproc)
pool.map(run, commands)
view raw multiproc.py hosted with ❤ by GitHub
#include <stdio.h>
#include <math.h>
double log_factorial(int i) {
double ans = 0;
if (i <= 1) return ans;
while (i > 1) ans += log(i--);
return ans;
}
int main(int argc, char** argv) {
int input = atoi(argv[1]);
printf("log(%d!) = %f\n", input, log_factorial(input));
return 0;
}
view raw run.c hosted with ❤ by GitHub