Click here to Skip to main content
Click here to Skip to main content

Convolutional Neural Network Workbench

By , 14 May 2012
 

Introduction

This article is about a Microsoft C# 4.0 WPF implementation of a framework that allows to create, train, and test convolutional neural networks against the MNIST dataset of handwritten digits or the CIFAR-10 dataset of 10 different natural objects. There is a magnificent article by Mike O'Neill on the The Code Project about the same subject. Without his great article and C++ demo code, this project wouldn't exist. I also relied heavily on Dr. Yann LeCun's paper: Gradient-Based Learning Applied to Document Recognition to understand more about the principles of convolutional neural networks and the reason why they are so successful in the area of machine vision. Mike O'Neill uses Patrice Simard's implementation where the subsampling step is integrated in the structure of the convolutional layer itself. Dr. Yann LeCun uses in his LeNet-5 a separate subsampling step, and also uses non-fully connected layers. The framework presented allows to use all types of layers, and has an additional Max-Pooling layer that you can use instead of plain Average-Pooling. The default squashing function used is tanh() and the value to train for is set to 0.8 because it is the value at the curvature of the second derivative of the used non-linearity so there is less saturation. The input images are all normalised [-1,1] and the input layer is at a fixed 32x32 window.

The Code

The main goal of this project was to build an enhanced and extended version of Mike O'Neill's excellent C++ project. This time written in C# 4.0 and using WPF with a simple MVVM pattern as the GUI instead of Windows Forms. I've included and used the WPF TaskDialog Wrapper from Sean A. Hanley instead of the Windows API Code Pack because the first is more compact and fit my needs perfectly. Also the Extended WPF Toolkit is used. For unzipping the CIFAR-10 dataset I used the open-source SharpDevelop SharpZipLib module. So Visual Studio 2010 and Windows Vista SP2 are the minimum requirements to use my application or just the operating system if you only use the setup. I also made maximal use of the parallel functionality offered in C# 4.0 by letting the user at all times choose how many logical cores are used in the parallel optimised code parts with a simple manipulation of the sliderbar next to the View combobox.

Using the code

Here is the example code to construct a LeNet-5 network in my code (see the InitializeDefaultNeuralNetwork() function in MainViewWindows.xaml.cs):

NeuralNetworks network = new NeuralNetworks("LeNet-5", 0.8D, LossFunctions.MeanSquareError, DataProviderSets.MNIST, 0.02D);
network.Layers.Add(new Layers(network, LayerTypes.Input, 1, 32, 32));
network.Layers.Add(new Layers(network, LayerTypes.Convolutional,ActivationFunctions.Tanh, 6, 28, 28, 5, 5));
network.Layers.Add(new Layers(network, LayerTypes.Subsampling, ActivationFunctions.AveragePoolingTanh, 6, 14, 14, 2, 2));

List<bool> mapCombinations = new List<bool>(16 * 6) 
{
 true, false,false,false,true, true, true, false,false,true, true, true, true, false,true, true,
 true, true, false,false,false,true, true, true, false,false,true, true, true, true, false,true,
 true, true, true, false,false,false,true, true, true, false,false,true, false,true, true, true,
 false,true, true, true, false,false,true, true, true, true, false,false,true, false,true, true,
 false,false,true, true, true, false,false,true, true, true, true, false,true, true, false,true,
 false,false,false,true, true, true, false,false,true, true, true, true, false,true, true, true
};

network.Layers.Add(new Layers(network, LayerTypes.Convolutional, ActivationFunctions.Tanh, 16, 10, 10, 5, 5, new Mappings(network, 2, mapCombinations)));
network.Layers.Add(new Layers(network, LayerTypes.Subsampling, ActivationFunctions.AveragePoolingTanh, 16, 5, 5, 2, 2));
network.Layers.Add(new Layers(network, LayerTypes.Convolutional, ActivationFunctions.Tanh, 120, 1, 1, 5, 5));
network.Layers.Add(new Layers(network, LayerTypes.FullyConnected, ActivationFunctions.Tanh, 10));
network.InitWeights();

Design View

CNNWBMNIST/DesignMNIST.png

CNNWBMNIST/DesignCIFAR.png

This is Design view where you can see how the network is defined and see the weights of all the layers. When you hover with the mouse over a single weight, a tooltip shows the corresponding weight or bias value. You can always refresh the weights graphic if you have changed the block size so you can see it in the prefered size.

Training View

CNNWBMNIST/TrainingMNIST.png

CNNWBMNIST/TrainingCIFARnew.png

This is Training view where you train the network. The 'Play' button gives you the 'Select Training Parameters' dialog where you can define the basic training parameters. The 'Training Scheme Editor' button gives you the possibility to fully define your own training schemes to experiment with. At any time, the training can be easily aborted by pressing the 'Stop' button. The 'Star' button will reset (forget) all the weight values.

CNNWBMNIST/MNISTTrainingParametersNew.png

CNNWBMNIST/TrainingSchemeEditorNew.png

Testing View

CNNWBMNIST/TestingMNISTnew.png

CNNWBMNIST/TestingCIFARnew.png

In Testing view, you can test your network and get a graphical confusion matrix that represents all the misses.

Calculate View

CNNWBMNIST/CalculateMNIST.png

CNNWBMNIST/CalculateCIFAR.png

In Calculate view, we can test a single digit or object with the desired properties and fire it through the network and get a graphical view of all the output values in every layer.

Final Words

I would love to see a DirectCompute 5.0 integration for offloading the highly parallel task of learning the neural network to a DirectX 11 compliant GPU if one is available. But I've never programmed with DirectX or any other shader based language before, so if there's anyone out there with some more experience in this area, any help is very welcome. I made an attempt to use a simple MVVM structure in this WPF application. In the Model folder, you can find the files for the neural network class and also a DataProvider class which deals with loading and providing the necessary MNIST and CIFAR-10 training and testing samples. There is also a NeuralNetworkDataSet class that is used by the project to load and save neural network definitions, weights, or both (full) from or to a file on disk. Then there is the View folder that contains the four different PageViews in the project and a global PageView which acts as a container for the different views (Design, Training, Testing, and Calculate). In the ViewModel folder, you will find a PageViewModelBase class where the corresponding four ViewModels are derived from. All the rest is found in the MainViewWindows.xaml.cs class. Hope there's someone out there who can actually use this code and improve on it. Extend it with an unsupervised learning stage for example (encoder/decoder construction), or implement a better loss-function (negative log likelihood instead of MSE); extend to more test databases; make use of more advanced squashing functions, etc.

History

1.0.2.5: (05-27-2012)

- Now you can download MyNet-16 (42 errors) weights file.

- Code cleaning and spelling corrections.

1.0.2.4: (05-14-2012)

- Fix: Download of the MNIST dataset now works for everybody. If you had problems with downloading, it's best to delete the CNNWB folder under My Documents and then run the latest version.

1.0.2.3: (05-10-2012)

- Fix: The Pattern Index value in Calculate View isn't set to zero anymore when changing to a different View.

1.0.2.2: (05-03-2012)

- Several important fixes for functionality previous version.

1.0.2.1: (04-30-2012)

- Added the possibility to switch every dataset from float to double and viceversa. Using a dataset in float reduces memory consumption quite a bit on big sets. If you have plenty of memory you can use a double dataset. The benefit of a double dataset is a slight speed advantage in training the network.

- Added a global setting of the default MNIST distortion parameters used.

- Better garbage collection.

1.0.2.0: (04-17-2012)

- Better garbage collection when switching between networks.

- PageViewModelBase.cs and the classes wich derive from it are cleaned from some unnecessary code.

- Refactoring & small fixes.

1.0.1.9: (04-10-2012)

- Bugfixes.

1.0.1.8: (04-07-2012)

- Reduced memory usage for every dataset.

- Bugfixes.

1.0.1.7: (03-17-2012)

- Fixed: Download of MNIST dataset.

- Fixed: Training Scheme Editor works now for the CIFAR-10 dataset.

1.0.1.6: (03-13-2012)

- Speed improvements in training the CNN.

- Speed improvements in creating the Design & Calculate graphic.

1.0.1.5: (02-26-2012)

- Memory consumption reduced.

1.0.1.4:

- Loading the CIFAR-10 dataset is now much faster.

- The performance of Design View is now better optimised for bigger networks.

- It's now possible to adjust the block size of the weight and output values graphic.

- In Design View you can refresh the weights graphic to the current block size.

1.0.1.3:

- Performance improvements in training networks

- Performance improvement in displaying Design View. (still to slow for big networks)

- Minor GUI changes

1.0.1.2:

- Now all the fully connected layers are displayed in Calculate View.

- Changing the background color is working properly now.

1.0.1.1:

- Now you can easily reset the weights values in Training View.

- By using Max-Pooling with the CIFAR-10 dataset the results are much better. I've also horizontal flipped each training pattern to double the size of the training set.

- Some minor fixes.

1.0.1.0:

- The CIFAR-10 Dataset of 10 natural objects in color is now fully supported.

- The weights in Design View are now correctly displayed. (still slow on big networks)

- The file format used to save and load weights, definitions, etc is changed and incompatible with previous versions.

1.0.0.1:

- Now you can see all the weight and bias values in every layer.

- Renaming some items so they make more sense (KernelTypes.Sigmoid => ActivationFunctions.Tanh)

- As a last layer you can use LeCun's RBF layer with fixed weights.

- Now it is possible to uses ActivationFunctions.AbsTanh to have a rectified convolutional layer.

1.0.0.0:

- Initial release

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Filip D'haene
Software Developer
Belgium Belgium
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
GeneralRe: CIFAR10 resultsmemberOve27 May '12 - 3:07 
Thanks for the answer and the details of the network. I saw there were many networks in InitializeDefaultNeuralNetwork(), but I didn't know which one gave the best results.
 
Is it possible for you to upload the network weights on some other site (wetransfer.com, transferbigfiles.com) and post the download link here?
Or maybe even send it by e-mail?
GeneralRe: CIFAR10 resultsmemberFilip D'haene27 May '12 - 5:46 
You can always reproduce the same or better result with some training yourself (say 24 epochs).
But if you give me an e-mail address I can always try sending it to you.
 
Filip
Questionprogram realizationmemberДарья Прокурат12 May '12 - 22:44 
1. You save to struct Connection index of Neuron and Weight. Why not to keep references?
2. enum LayerTypes. Why not to use inheritance? With enum works quicker? or for use enum instead of inheritance there were no reasons?
AnswerRe: program realizationmemberFilip D'haene13 May '12 - 1:44 
1) I've used struct instead of a class for the connections because it doubled my speed of the cnn's. (Connection was in my early versions a full class, but much slower)
 
2) You're right. Thats probably a better manner (faster) instead of using enums. Smile | :)
 
Filip
GeneralRe: program realizationmemberДарья Прокурат13 May '12 - 2:57 
1. No, I ask not about struct vs class. I want to know, why you don't do such:
public struct Connection
{
    public Neuron ToNeuron { get; private set; }
    public Weight WithWeight { get; private set;}
 
    public Connection(Neuron toNeuron, Weight toWeight):this()
    {
        ToNeuron = toNeuron;
        WithWeight = toWeight;
    }
    public Connection(Weight toWeight): this()
    {
        WithWeight = toWeight;
        ToNeuron = new Neuron { Output = 1 };
    }
}
If there any reason to save index?
public int ToNeuronIndex;
public int ToWeightIndex;

AnswerRe: program realization [modified]memberFilip D'haene13 May '12 - 4:06 
Well, what your suggesting is an other valid way of doing the same thing.You don't have the need anymore to have references to some neuron or weight index. But you have to consider that the storage of pair of integers is smaller. I'm not so sure it would speed up things. D'Oh! | :doh:

modified 13 May '12 - 12:38.

QuestionActivationFunctionsmemberДарья Прокурат7 May '12 - 10:49 
Can you give any link or write math formulas for calculation function with different ActivationFunctions?
For example, this is part to ActivationFunctions.MaxPoolingTanh in Layer.Calculate()
double bias = 0D;
double weight = 1D;
List<double> previousOutputs = new List<double>(4);
foreach (Connection connection in neuron.Connections)
{
    if (connection.ToNeuronIndex == int.MaxValue)
        bias = Weights[connection.ToWeightIndex].Value;
    else
    {
        weight = Weights[connection.ToWeightIndex].Value;
        previousOutputs.Add(PreviousLayer.Neurons[connection.ToNeuronIndex].Output);
    }
}
neuron.Output = Sigmoid((previousOutputs.Max() * weight) + bias);
 
in foreach we set weight a lot of times but use in outside of foreach. so it always will be last value. is there mistake? If not, by with math formula it is writen? Why we take max Output value and multiply on last weight and ignore all other weights?
And why we set 4 in "new List(4)"?
AnswerRe: ActivationFunctionsmemberFilip D'haene7 May '12 - 13:07 
Well there's no mistake in the code because the weight value is shared and has exactly the same value every time unless connection.ToNeuronIndex == int.MaxValue. The new List<double>(4) is arbitrary chosen and doesn't limit the capacity of the List. It will work with less and more than 4 outputs. For a link to more activation functions you best use google and see what's out there. On Dr. LeCun's site you can find plethora of good papers on machine learning.
 
Hope this somehow helps, Smile | :)
Filip
GeneralRe: ActivationFunctionsmemberДарья Прокурат10 May '12 - 5:34 
LayerTypes.Subsampling
ActivationFunctions.AveragePoolingTanh
foreach (Connection connection in neuron.Connections)
    if (connection.ToNeuronIndex == int.MaxValue)
        dSum += Weights[connection.ToWeightIndex].Value;
    else
        dSum += Weights[connection.ToWeightIndex].Value *
                PreviousLayer.Neurons[connection.ToNeuronIndex].Output * 
                SubsamplingScalingFactor;
neuron.Output = Sigmoid(dSum);
 
Why we use SubsamplingScalingFactor? Why don't wait while Weights changes to become like:
Weights[connection.ToWeightIndex].Value *= SubsamplingScalingFactor

GeneralRe: ActivationFunctionsmemberFilip D'haene10 May '12 - 5:45 
I think your missing the point of the Calculate step. We want to calculate the new output
values, with the current weight values. We are not gonna change our weight values here.
Only in the backpropagate step we're gonna changes our weight values.
GeneralRe: ActivationFunctionsmemberДарья Прокурат10 May '12 - 6:24 
Yes. I know. But SubsamplingScalingFactor is const for layer.
So why we can't changes wieght in layer initialization by multiply on SubsamplingScalingFactor?
GeneralRe: ActivationFunctionsmemberFilip D'haene10 May '12 - 6:50 
I'm really sorry but I don't quite understand your question. What do you mean by
layer initialisation? Do you mean setting the initial weight values of the cnn (SetInitalWeights function)? If so, it's up to you if you want other initial random weights for the subsampling layers.
GeneralRe: ActivationFunctionsmemberДарья Прокурат10 May '12 - 7:41 
Yes, I mean setting the initial weight values of the cnn (SetInitalWeights function).
If we in the end of SetInitalWeights for this layer add
Weights[connection.ToWeightIndex].Value *= SubsamplingScalingFactor
and in all other places delete
* SubsamplingScalingFactor
will it works like your variant? Or am I missing something important about SubsamplingScalingFactor?
AnswerRe: ActivationFunctionsmemberFilip D'haene10 May '12 - 8:01 
No, that's not going to work. I can only recommend
to read Dr. LeCun excellent paper.
Questioncrashes while downloading training imagesmemberSperneder Patrick5 May '12 - 8:33 
Hello Filip!
i hope you can help me, i get an AggregateException while the application is starting up.
The exception occurs right before downloading the training images.
AnswerRe: crashes while downloading training imagesmemberFilip D'haene5 May '12 - 9:01 
You must delete the CNNWB folder in My Documents and then restart the latest version of
the program. Maybe your internet connection was diconnected or so.
 
Hope this somehow helps, Smile | :)
Filip
GeneralRe: crashes while downloading training imagesmemberSperneder Patrick8 May '12 - 3:25 
Hello!
Deleting the folder and running the app again did not solve the problem.
My internet-connection definitely is stable. Frown | :(
Somehow the thrown exception sounds to me as if there is a problem with multithreading ? Confused | :confused:
AnswerRe: crashes while downloading training imagesmemberFilip D'haene8 May '12 - 4:13 
Can you tell me the operating system you're using (including the service pack and 32-bit or 64bit version) and if
you're using the latest version of the program. Also are you running it from the source code with Visual Studio 2010 or just using the setup version.
 
(I remember when changing to .NET 4.5 in Windows 8 Preview I was getting errors when downloading the needed files)
GeneralRe: crashes while downloading training imagesmemberSperneder Patrick10 May '12 - 7:30 
Hello Filip,
Sorry for the delay, i'm using Windows 7 Home Premium SP1, 64bit. Running the app with attached Debugger(VS 2010 Ultimate)
and also running it from the setup leads to the same error.
Installed .NET is 4.0 with latest Servicepacks..
weird... Dead | X|
regards Patrick
AnswerRe: crashes while downloading training images [modified]memberFilip D'haene10 May '12 - 8:16 
Thats very weird indeed, because I'm runny on exaxcly the same operating system and version of Visual Studio. Two things I'm curious about: wich type of processor do you have and if you're sure your using the .NET Framework 4 Client Profile in the Application Properies of the CNNWB project?
 
Filip

modified 10 May '12 - 14:43.

GeneralRe: crashes while downloading training imagesmemberSperneder Patrick10 May '12 - 9:39 
Big Grin | :-D this seems to be an evil .NET trap....
 
Processor : Intel Pentium CPU B960 @2.20GHz 2.20GHz ( Dualcore )
and yes, i am using the .NET 4.0 Client Profile
if you send me your e-mail adress, i can send you a stacktrace and show you exactly where the app shoots herself in the knee... Poke tongue | ;-P
mine is patrick.sperneder@gmx.at
 
regards
P.
AnswerRe: crashes while downloading training imagesmemberFilip D'haene14 May '12 - 18:49 
The lastest version should resolve your download problems.
 
greetings,
Filip
QuestionDownload links do not work... please checkmemberfawzi_masri10 Apr '12 - 10:42 
source code links are not functional,can you please check it.
 

thanks,
AnswerRe: Download links do not work... please checkmemberFilip D'haene10 Apr '12 - 10:50 
This is a CodeProject website issue that for some unknown reason don't let you
download the files you want. I've changed nothing to my article in the mean time.
downloading was working fine a couple of hours ago! D'Oh! | :doh:
 
I hope it will be fixed soon!
GeneralRe: Download links do not work... please checkmemberfawzi_masri10 Apr '12 - 10:59 
I thought so since the trail shows that you just did fix it....
ok, i will wait and see...

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 14 May 2012
Article Copyright 2010 by Filip D'haene
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid