- •Введение
- •1.Исскуственные нейронный сети
- •Многослойный персептрон
- •2.Анализ задания
- •2.1 Возможности реализации ascii art без применения инс
- •2.2 Предпосылки для применения инс
- •2.3 Описание системы и подготовка обучающей выборки
- •1.3 Проектирование
- •1.3.1 Проектирование инс.
- •3.2 Проектирование приложения
- •4 Тестирование
- •5.Подведение итогов
4 Тестирование
Для тестирования сконфигурируем приложение следующим образом:
Использовать кодирование цвета – Да
Масштабировать изображение – Да, на величину 600
Дублировать символы – Да
Использовать предобработку – Да
Символ закрашивания – ‘#’
Пустой символ – ‘ ‘
Начнем с самого простого случая – цветное изображение на белом фоне:
Изображение |
Результат |
|
|
|
|
|
|
На данном типе образов система работает корректно. Потеря информации почти не отсутствует.
Далее подадим изображение с фоном, который достаточно сильно отличается от целевого объекта
Изображение |
Результат |
|
|
|
|
|
|
Часть изображения, связанная с фоном теряется. Также теряются фрагменты, если они прилегают близко к фону. Это обусловлено в первую очередь недостатками алгоритма удаления цвета. Объект, который находиться в фокусе по-прежнему можно однозначно узнать.
Далее подадим изображения, относящиеся к природе. На данных примерах фон сливается с объектом, что максимально усложняет процесс распознавания.
Изображение |
Результат |
|
|
|
|
|
|
Система плохо кодирует такого рода изображения. Это связано, во-первых, с значительной потерей информации при удалении цвета, а во-вторых в том, что присутствует много объектов, и на общей картине и с большой вероятностью в один фрагмент попадают части разных, возможно даже не связанных объектов. В таком случае классифицировать что на изображении нам помогает по большому счету цвет.
Под конец приведем ещё несколько интересных частных случаев.
Рисунок 4.1: Входное изображение черно-белое
Рисунок 4.2: Входное изображение – Фото
Рисунок 4.3: Темное изображение на темном фоне
Рисунок 4.4: Светлое изображение на светлом фоне.
Рисунок 4.5: Изображения, содержащие большой объем информации.
5.Подведение итогов
В результате выполнения курсового была разработана и реализована библиотека для конструирования нейронных сетей. Особое внимание уделялось архитектуре, чтобы в итоге можно было собирать типовые нейронные сети, а при необходимости доопределять нужный функционал и интегрировать его с уже существующим. Но пришлось жертвовать быстродействием: так как повсеместно был использован динамический полиморфизм и наследование, код потерял в быстродействии и стал более раздутым, чем не расширяемые аналоги.
Также была разработана и реализована система для кодирования изображений в ascii art. Как было показано выше, система работает хорошо на тех изображениях, на которых, хорошо работает алгоритм удаления цвета. Если большая часть информации теряется после применения алгоритма удаления цвета, то и кодирование будет не качественным. Если изображение содержит большое количество объектов кодирование также работает не лучшим образом. В остальных же случаях был получен приемлемый результат: результат кодирования внешне похож на поданное изображение.
Применение ИНС в данной работе уменьшило недостатки описанных в начале работы методов перевода изображения в ascii art. Благодаря применения ИНС результат можно хранить в текстовых форматах, не поддерживающих цвет, передавать в компактном виде по сети, выводить на консоль, использовать как замены художника в советующем направлении искусства.
Стоит отметить что ручная работа, очевидно, качественнее результата работы системы. Однако ручная работа требует высоких затрат труда и времени, в то время как система кодирует очень быстро. Можно совместить работу программы и человека, что ускорит процесс выполнения работы художника.
Были показаны возможности восстановления изображения, хоть и с потерями, что дает предпосылки возможности алгоритма сжатия с потерями на основании описанной технологии.
Используемые источники:
1) Головко В.А. Нейронные сети: обучение, организация и применение
2) Павел Нестеров. Сборник статей про нейронные сети на habrahabr.
3) Муравьев Г.Л. Моделирование систем. Курс лекций. – БрГТУ, Брест, 2003.
Приложение А
[DataContract]
public class SimpleBackPropogation : DifferintiableLearningConfig, ILearn<double>
{
public SimpleBackPropogation(DifferintiableLearningConfig config)
: base(config)
{
}
/// <summary>
/// Обучения для отображения входных сигналов в выходные
/// </summary>
/// <param name="network"></param>
/// <param name="data"></param>
/// <param name="check"></param>
public void Train(INeuralNetwork<double> network, IList<NeuroImage<double>> data, IList<NeuroImage<double>> check)
{
//получаем удобную ссылку на сеть
MultiLayer net = network.Layer as MultiLayer;
this.CurrentEpoch = 0;
this.Succes = false;
double lastError = 0;
double[][] y = new double[data.Count][];
do
{
lastError = CurrentError;
//Перемешивание образов из выборки для данной эпохи
if (UseRandomShuffle == true)
{
RandomShuffle(ref data);
}
for (int curr = 0; curr < data.Count; curr++)
{
//считаем текущие выходы и импульсы на каждом нейроне
y[curr] = network.Compute(data[curr].Input);
//делаем оценку для текущего образа, и если погрешность допустима,идем
//на следующий образ. Данная модификация ускоряет процесс обучения и
//уменьшает среднеквадратичную ошибку
if (Compare(data[curr].Output, network.Layer[net.Layer.Length - 1].LastPulse) == true)
{
continue;
}
//считаем ошибку выходного слоя
OutputLayerError(data, net, curr);
//считаем ошибки скрытых слоев
HiddenlayerError(net);
//модифицируем веса и пороги
ModifyParametrs(data, net, curr);
}
//расчитываем ошибку
ComputeError(data, network,y);
//производим перерасчет на основании регуляризации
ComputeRegularizationError(data, net);
System.Console.WriteLine("Eposh #" + CurrentEpoch.ToString() +
" finished; current error is " + CurrentError.ToString()
);
CurrentEpoch++;
}
while (CurrentEpoch < MaxEpoch && CurrentError > MinError &&
Math.Abs(CurrentError - lastError) > MinChangeError);
ComputeRecognizeError(check, network);
}
private void ComputeRecognizeError(IList<NeuroImage<double>> check, INeuralNetwork<double> network)
{
int accept = 0;
double LastError = 0;
RecogniseError = 0;
for (int i = 0; i < check.Count; i++)
{
double[] realOutput = network.Compute(check[i].Input);
double[] result = new double[realOutput.Length];
LastError = ErrorFunction.Compute(check[i].Output, realOutput);
RecogniseError += LastError;
double max = realOutput.Max();
int index = realOutput.ToList().IndexOf(max);
result[index] = 1;
if (ArrayCompare(result, check[i].Output) == true)
{
accept++;
}
}
RecognisePercent = (double)accept / (double)check.Count;
RecogniseError /= 2;
}
public static bool ArrayCompare(double[] a, double[] b)
{
if (a.Length == b.Length)
{
for (int i = 0; i < a.Length; i++)
{
if (a[i] != b[i]) { return false; };
}
return true;
}
return false;
}
/// <summary>
/// Учет регуляризации для расчета среднеквадратичной ошибки
/// </summary>
/// <param name="data">обучающая выборка</param>
/// <param name="net">сеть</param>
private void ComputeRegularizationError(IList<NeuroImage<double>> data, MultiLayer net)
{
if (Math.Abs(Regularization - 0d) > Double.Epsilon)
{
double reg = 0;
for (int layerIndex = 0; layerIndex < net.Layer.Length; layerIndex++)
{
for (int neuronIndex = 0; neuronIndex < net.Layer[layerIndex].Neuron.Length; neuronIndex++)
{
for (int weightIndex = 0; weightIndex < net.Layer[layerIndex].Neuron[neuronIndex].Weights.Length; weightIndex++)
{
reg += net.Layer[layerIndex].Neuron[neuronIndex].Weights[weightIndex] *
net.Layer[layerIndex].Neuron[neuronIndex].Weights[weightIndex];
}
}
}
CurrentError += Regularization * reg / (2 * data.Count);
}
}
/// <summary>
/// Расчет ошибки сети
/// </summary>
/// <param name="data">обучающая выборка</param>
/// <param name="network">сеть</param>
private void ComputeError(IList<NeuroImage<double>> data, INeuralNetwork<double> network, double[][] realOutput)
{
int accept = 0;
double LastError = 0;
CurrentError = 0;
for (int i = 0; i < data.Count; i++)
{
LastError = ErrorFunction.Compute(data[i].Output, realOutput[i]);
CurrentError += LastError;
}
TeachPercent = (double)accept / (double)data.Count;
CurrentError /= 2;
}
/// <summary>
/// Модификация настраиваемых параметров сети
/// </summary>
/// <param name="data">обучающая выборка</param>
/// <param name="net"><сеть/param>
/// <param name="curr">номер текущего образа</param>
private void ModifyParametrs(IList<NeuroImage<double>> data, MultiLayer net, int curr)
{
for (int i = net.Layer.Length - 1; i >= 0; i--)//по всем слоям в обратном порядке
{
for (int j = 0; j < net[i].Neuron.Length; j++)//для каждого нейрона
{
double temp = Step * net[i].Neuron[j].CurrentError
* net[i].Neuron[j].ActivationFunction.Derivative(net[i].Neuron[j].LastPulse);
net[i].Neuron[j].Offset += temp;
for (int k = 0; k < net[i].Neuron[j].Weights.Length; k++)//для каждого веса
{
if (i == 0)
{
net[i].Neuron[j].Weights[k] -= temp
* data[curr].Input[k] + Regularization * net[i].Neuron[j].Weights[k] / data.Count;
}
else
{
net[i].Neuron[j].Weights[k] -= temp
* net[i - 1].Neuron[k].LastState + Regularization * net[i].Neuron[j].Weights[k] / data.Count;
}
}
}
}
}
/// <summary>
/// Расчет ошибки скрытых слоев
/// </summary>
/// <param name="net">сеть</param>
private static void HiddenlayerError(MultiLayer net)
{
for (int k = net.Length - 2; k >= 0; k--)
{
//для каждого нейрона
for (int j = 0; j < net[k].Neuron.Length; j++)
{
net.Layer[k].Neuron[j].CurrentError = 0;
//сумма по всем нейронам следующего слоя
for (int i = 0; i < net[k + 1].Neuron.Length; i++)
{
//errorj = sum_for_i(errori * F'(Sj) * wij)
net.Layer[k].Neuron[j].CurrentError += net.Layer[k + 1].Neuron[i].CurrentError
* net.Layer[k + 1].Neuron[i].Weights[j] *
net.Layer[k + 1].Neuron[i].ActivationFunction.Derivative(net.Layer[k + 1].Neuron[i].LastPulse);
}
}
}
}
/// <summary>
/// Расчет ошибки выходного слоя
/// </summary>
/// <param name="data">обучающая выборка</param>
/// <param name="net">сеть</param>
/// <param name="curr">номер текущего образа</param>
///
private void OutputLayerError(IList<NeuroImage<double>> data, MultiLayer net, int curr)
{
int last = net.Layer.Length - 1;
//ошибка выходного слоя по каждому нейрону
for (int j = 0; j < net.Layer[last].Neuron.Length; j++)
{
net.Layer[last].Neuron[j].CurrentError = 0;
net.Layer[last].Neuron[j].CurrentError = ErrorFunction.Derivative(net.Layer[last].Neuron[j].LastState, data[curr].Output[j]);
}
}
private void RandomShuffle(ref IList<NeuroImage<double>> data)
{
Random gen = new Random();
int ind1, ind2;
for (int i = 0; i < data.Count; i++)
{
ind1 = gen.Next(0, data.Count);
ind2 = gen.Next(0, data.Count);
Swap(data, ind1, ind2);
}
}
private void Swap(IList<NeuroImage<double>> data, int ind1, int ind2)
{
NeuroImage<double> temp = data[ind1];
data[ind1] = data[ind2];
data[ind2] = temp;
}
private bool Compare(double[] p1, double[] p2)
{
double error = 0.0;
for (int i = 0; i < p1.Length; i++)
{
error += (p1[i] - p2[i]) * (p1[i] - p2[i]);
}
error /= 2;
if (error < OneImageMinError)
return true;
else
return false;
}
}
Приложение Б:
private void cloudButton1_Click(object sender, EventArgs e)
{
UpdateParametrizedSymbol();
toolStripProgressBar1.ProgressBar.Show();
Application.DoEvents();
source.Colors.Clear();
source.Text = null;
Task.Run(
() =>
{
Bitmap image = new Bitmap(pictureBox1.Image);
if (System.IO.File.Exists("SymbolRecognise.json") == false)
{
MessageBox.Show("Ошибка! Не найден файл SymbolRecognise.json");
return;
}
if (System.IO.File.Exists("ColorRecognise.json") == false)
{
MessageBox.Show("Ошибка! Не найден файл ColorRecognise.json");
return;
}
var SymbolNetwork = Neuro.MLP.MultiLayerNeuralNetwork.Load("SymbolRecognise.json");
var ColorNetwork = Neuro.MLP.MultiLayerNeuralNetwork.Load("ColorRecognise.json");
System.Drawing.Size blockSize = new Size(10, 10);
System.Drawing.Size metrics = Work.BitmapWorker.Round(image.Size, blockSize);
if (source.DefaultZoom == true)
{
metrics = Work.BitmapWorker.RecalculateSize(image.Size, source.WidthValue);
image = Work.BitmapWorker.Zoom(image, metrics);
}
if (image.Height % blockSize.Height != 0)
{
metrics = Work.BitmapWorker.Round(image.Size, blockSize);
image = Work.BitmapWorker.Resize(image, metrics);
}
int blockPerLine = Convert.ToInt32((float)image.Width / (float)blockSize.Width);
int BlockAmount = Convert.ToInt32((float)image.Width / (float)blockSize.Width * (float)image.Height / (float)blockSize.Height);
double[] input = new double[100];
Bitmap ImageToEncoding;
if (source.UsePreworking == true)
ImageToEncoding = Work.BitmapWorker.ColorToGray(image);
else
ImageToEncoding = image;
Bitmap temp, encodeTemp;
Rectangle rect;
for (int i = 0; i < BlockAmount; i++)
{
rect = GetBlockRect(image, i);
temp = Work.BitmapWorker.Cut(image, rect);
encodeTemp = Work.BitmapWorker.Cut(ImageToEncoding, rect);
if (source.SixteenColorMode == true)
{
source.Colors.Add(ColorNetwork.Compute(temp));
}
else
{
source.Colors.Add(temp.GetPixel(5, 5));
}
input = CreateInput(encodeTemp);
var res = SymbolNetwork.Compute(input);
double max = res.Max();
int index = res.ToList().IndexOf(max);
source.Text += source.Images[index];
if (source.Doublicate)
{
source.Text += source.Images[index];
source.Colors.Add(source.Colors.Last());
}
if (i % blockPerLine == blockPerLine - 1)
source.Text += '\n';
}
Form2 dlg = new Form2(source.Text, source.Colors);
toolStripProgressBar1.ProgressBar.InvokeIfRequired(() =>
{
// Do anything you want with the control here
toolStripProgressBar1.ProgressBar.Hide();
});
dlg.ShowDialog();
}
);
}
