Image Classification
Image Classification
cs 1
1 using System;
2 using System.IO;
3 using static System.Net.Mime.MediaTypeNames;
4
5 namespace ImageClassification
6 {
7 class Program
8 {
9 static int ImageSize = 32 * 32 * 3; // CIFAR-10 image size
10 static int NumClasses = 10; // 10 output classes
11 static int HiddenSize = 128; // Hidden layer size
12 static double LearningRate = 0.001;
13 static int Epochs = 10;
14
15 static double[,] WeightsInputHidden = new double[ImageSize,
HiddenSize];
16 static double[,] WeightsHiddenOutput = new double[HiddenSize,
NumClasses];
17 static double[] BiasHidden = new double[HiddenSize];
18 static double[] BiasOutput = new double[NumClasses];
19
20 static Random random = new Random();
21
22 static void Main(string[] args)
23 {
24 // Step 1: Initialize weights and biases
25 InitializeWeights();
26
27 // Step 2: Load CIFAR-10 dataset
28 double[][] trainingData = LoadTrainingData();
29 int[] trainingLabels = LoadTrainingLabels();
30
31 // Step 3: Train the neural network
32 for (int epoch = 0; epoch < Epochs; epoch++)
33 {
34 double totalLoss = 0.0;
35 for (int i = 0; i < trainingData.Length; i++)
36 {
37 // Prepare data
38 double[,] input2D = ConvertTo2D(trainingData[i]); //
Convert 1D to 2D
39 var (hiddenOutput, cnnOutput) = ForwardPassCNN
(input2D); // Get both hiddenOutput and output
40
41 // Compute loss and gradients
42 double[] target = new double[NumClasses];
43 target[trainingLabels[i]] = 1.0; // One-hot encoding
44 double[] lossGradient = new double[NumClasses];
45 for (int j = 0; j < NumClasses; j++)
...os\ImageClassification\ImageClassification\Program.cs 2
46 {
47 lossGradient[j] = cnnOutput[j] - target[j];
48 totalLoss += 0.5 * Math.Pow(lossGradient[j], 2);
49 }
50
51 // Flatten the input2D for Backpropagate
52 double[] flattened = Flatten(input2D);
53
54 // Pass the correct hiddenOutput to Backpropagate
55 Backpropagate(flattened, hiddenOutput, cnnOutput,
lossGradient);
56 }
57 Console.WriteLine($"Epoch {epoch + 1}: Loss = {
totalLoss / trainingData.Length}");
58 }
59
60 // Step 4: Evaluate the model with micro-accuracy
61 double[][] testData = LoadTestData();
62 int[] testLabels = LoadTestLabels();
63 double correctPredictions = 0;
64
65 for (int i = 0; i < testData.Length; i++)
66 {
67 double[,] input2D = ConvertTo2D(testData[i]);
68 var (_, cnnOutput) = ForwardPassCNN(input2D); // Get only
the output
69
70 int predictedLabel = Array.IndexOf(cnnOutput, MaxValue
(cnnOutput)); // Predict the label
71 if (predictedLabel == testLabels[i])
72 {
73 correctPredictions++;
74 }
75 }
76
77 double microAccuracy = correctPredictions / testData.Length;
78 Console.WriteLine($"Micro-Averaged Accuracy: {(
microAccuracy * 100):.2f}%");
79 }
80
81
82 static void InitializeWeights()
83 {
84 for (int i = 0; i < ImageSize; i++)
85 for (int j = 0; j < HiddenSize; j++)
86 WeightsInputHidden[i, j] = random.NextDouble() * 0.01;
87
88 for (int i = 0; i < HiddenSize; i++)
89 for (int j = 0; j < NumClasses; j++)
...os\ImageClassification\ImageClassification\Program.cs 3
90 WeightsHiddenOutput[i, j] = random.NextDouble() *
0.01;
91 }
92
93
94 static double[] Softmax(double[] inputs)
95 {
96 double[] outputs = new double[inputs.Length];
97 double expSum = 0.0;
98
99 foreach (double value in inputs)
100 {
101 expSum += Math.Exp(value);
102 }
103
104 for (int i = 0; i < inputs.Length; i++)
105 {
106 outputs[i] = Math.Exp(inputs[i]) / expSum;
107 }
108
109 return outputs;
110 }
111 static void Backpropagate(double[] flattenedInput, double[]
hiddenOutput, double[] output, double[] lossGradient)
112 {
113 //Console.WriteLine("Starting Backpropagate...");
114 //Console.WriteLine($"FlattenedInput size:
{flattenedInput.Length}");
115 //Console.WriteLine($"HiddenOutput size:
{hiddenOutput.Length}");
116 //Console.WriteLine($"Output size: {output.Length}");
117 //Console.WriteLine($"LossGradient size:
{lossGradient.Length}");
118
119 if (hiddenOutput.Length != HiddenSize)
120 {
121 throw new Exception($"Mismatch: hiddenOutput size
({hiddenOutput.Length}) does not match HiddenSize
({HiddenSize}).");
122 }
123
124 double[] hiddenGradient = new double[HiddenSize];
125
126 // Update output layer weights
127 for (int col = 0; col < NumClasses; col++)
128 {
129 for (int row = 0; row < HiddenSize; row++)
130 {
131 WeightsHiddenOutput[row, col] -= LearningRate *
...os\ImageClassification\ImageClassification\Program.cs 4
lossGradient[col] * hiddenOutput[row];
132 }
133 BiasOutput[col] -= LearningRate * lossGradient[col];
134 }
135
136 // Compute hidden layer gradient
137 for (int row = 0; row < HiddenSize; row++)
138 {
139 for (int col = 0; col < NumClasses; col++)
140 {
141 hiddenGradient[row] += lossGradient[col] *
WeightsHiddenOutput[row, col];
142 }
143 hiddenGradient[row] *= (1 - Math.Pow(hiddenOutput[row],
2)); // Derivative of tanh
144 }
145
146 // Update input layer weights
147 for (int row = 0; row < HiddenSize; row++)
148 {
149 for (int col = 0; col < flattenedInput.Length; col++)
150 {
151 WeightsInputHidden[col, row] -= LearningRate *
hiddenGradient[row] * flattenedInput[col];
152 }
153 BiasHidden[row] -= LearningRate * hiddenGradient[row];
154 }
155 }
156
157
158 static double MaxValue(double[] array)
159 {
160 double max = array[0];
161 foreach (double value in array)
162 if (value > max) max = value;
163 return max;
164 }
165
166 // Load training data
167 public static double[][] LoadTrainingData()
168 {
169 string[] batches = {
170 "data_batch_1.bin",
171 "data_batch_2.bin",
172 "data_batch_3.bin",
173 "data_batch_4.bin",
174 "data_batch_5.bin"
175 };
176
...os\ImageClassification\ImageClassification\Program.cs 5
177 return LoadBatchData(batches);
178 }
179
180 // Load training labels
181 public static int[] LoadTrainingLabels()
182 {
183 string[] batches = {
184 "data_batch_1.bin",
185 "data_batch_2.bin",
186 "data_batch_3.bin",
187 "data_batch_4.bin",
188 "data_batch_5.bin"
189 };
190
191 return LoadBatchLabels(batches);
192 }
193
194 // Load test data
195 public static double[][] LoadTestData()
196 {
197 string[] batches = { "test_batch.bin" };
198 return LoadBatchData(batches);
199 }
200
201 // Load test labels
202 public static int[] LoadTestLabels()
203 {
204 string[] batches = { "test_batch.bin" };
205 return LoadBatchLabels(batches);
206 }
207
208 // Helper method to load batch data
209
210 private static double[][] LoadBatchData(string[] batchFiles)
211 {
212 var images = new System.Collections.Generic.List<double[]>();
213
214 foreach (var file in batchFiles)
215 {
216 if (!File.Exists(file))
217 throw new FileNotFoundException($"File not found:
{file}");
218
219 using (var reader = new BinaryReader(File.Open(file,
FileMode.Open)))
220 {
221 while (reader.BaseStream.Position <
reader.BaseStream.Length)
222 {
...os\ImageClassification\ImageClassification\Program.cs 6
223 // Read label (1 byte, discard here)
224 reader.ReadByte();
225
226 // Read image data (3072 bytes)
227 byte[] imageBytes = reader.ReadBytes(ImageSize);
228
229 // Debug raw values before normalization
230 //Console.WriteLine($"Raw Pixel Values:
{string.Join(", ", imageBytes.Take(10))}");
231
232 // Normalize image data to [0, 1]
233 double[] image = new double[ImageSize];
234 for (int i = 0; i < ImageSize; i++)
235 {
236 image[i] = imageBytes[i] / 255.0;
237 }
238
239 // Debug normalized values
240 //Console.WriteLine($"Normalized Pixel Values:
{string.Join(", ", image.Take(10))}");
241
242 images.Add(image);
243 }
244 }
245 }
246
247 return images.ToArray();
248 }
249 // Helper method to load batch labels
250 private static int[] LoadBatchLabels(string[] batchFiles)
251 {
252 var labels = new System.Collections.Generic.List<int>();
253
254 foreach (var file in batchFiles)
255 {
256 using (var reader = new BinaryReader(File.Open(file,
FileMode.Open)))
257 {
258 while (reader.BaseStream.Position <
reader.BaseStream.Length)
259 {
260 // Read label (1 byte)
261 labels.Add(reader.ReadByte());
262
263 // Skip image data (3072 bytes)
264 reader.BaseStream.Seek(ImageSize,
SeekOrigin.Current);
265 }
266 }
...os\ImageClassification\ImageClassification\Program.cs 7
267 }
268
269 return labels.ToArray();
270 }
271 #region ADDED CODE
272 static (double[] hiddenOutput, double[] output) ForwardPassCNN
(double[,] image)
273 {
274 //Console.WriteLine("Starting ForwardPassCNN...");
275
276 // Step 1: Apply convolution
277 double[,] kernel = InitializeKernel();
278 // Console.WriteLine("Kernel initialized.");
279 double[,] convOutput = Convolve(image, kernel);
280 // Console.WriteLine($"Convolution output size:
{convOutput.GetLength(0)}x{convOutput.GetLength(1)}");
281
282 // Step 2: Apply pooling
283 double[,] pooledOutput = MaxPool(convOutput, 2);
284 // Console.WriteLine($"Pooling output size:
{pooledOutput.GetLength(0)}x{pooledOutput.GetLength(1)}");
285
286 // Step 3: Flatten pooled output
287 double[] flattened = Flatten(pooledOutput);
288 //Console.WriteLine($"Flattened output size:
{flattened.Length}");
289
290 // Step 4: Pass through fully connected layers
291 var (hiddenOutput, output) = ForwardPassFullyConnected
(flattened);
292 //Console.WriteLine("Completed ForwardPassCNN.");
293 return (hiddenOutput, output);
294 }
295 static double[,] Convolve(double[,] input, double[,] kernel)
296 {
297 int kernelSize = kernel.GetLength(0);
298 int outputSize = input.GetLength(0) - kernelSize + 1;
299 double[,] output = new double[outputSize, outputSize];
300
301 for (int i = 0; i < outputSize; i++)
302 {
303 for (int j = 0; j < outputSize; j++)
304 {
305 double sum = 0.0;
306 for (int ki = 0; ki < kernelSize; ki++)
307 {
308 for (int kj = 0; kj < kernelSize; kj++)
309 {
310 sum += input[i + ki, j + kj] * kernel[ki, kj];
...os\ImageClassification\ImageClassification\Program.cs 8
311 }
312 }
313 output[i, j] = Math.Max(0, sum); // ReLU activation
314 }
315 }
316 return output;
317 }
318 static double[,] MaxPool(double[,] input, int poolSize)
319 {
320 int outputSize = input.GetLength(0) / poolSize;
321 double[,] output = new double[outputSize, outputSize];
322
323 for (int i = 0; i < outputSize; i++)
324 {
325 for (int j = 0; j < outputSize; j++)
326 {
327 double max = double.MinValue;
328 for (int pi = 0; pi < poolSize; pi++)
329 {
330 for (int pj = 0; pj < poolSize; pj++)
331 {
332 max = Math.Max(max, input[i * poolSize + pi,
j * poolSize + pj]);
333 }
334 }
335 output[i, j] = max;
336 }
337 }
338 return output;
339 }
340 static double[] Flatten(double[,] pooledOutput)
341 {
342 int rows = pooledOutput.GetLength(0);
343 int cols = pooledOutput.GetLength(1);
344 double[] flattened = new double[rows * cols];
345
346 for (int i = 0; i < rows; i++)
347 {
348 for (int j = 0; j < cols; j++)
349 {
350 flattened[i * cols + j] = pooledOutput[i, j];
351 }
352 }
353 return flattened;
354 }
355 static double[,] InitializeKernel()
356 {
357 int kernelSize = 3; // Example: 3x3 kernel
358 double[,] kernel = new double[kernelSize, kernelSize];
...os\ImageClassification\ImageClassification\Program.cs 9
359
360 Random random = new Random();
361 for (int i = 0; i < kernelSize; i++)
362 {
363 for (int j = 0; j < kernelSize; j++)
364 {
365 kernel[i, j] = random.NextDouble() * 0.01; // Small
random values
366 }
367 }
368
369 return kernel;
370 }
371 static (double[] hiddenOutput, double[] output)
ForwardPassFullyConnected(double[] flattened)
372 {
373 if (flattened.Length == 0)
374 {
375 throw new Exception("Flattened input is empty. Check your
CNN pipeline.");
376 }
377
378 double[] hiddenOutput = new double[HiddenSize];
379 for (int i = 0; i < HiddenSize; i++)
380 {
381 for (int j = 0; j < flattened.Length; j++)
382 {
383 hiddenOutput[i] += flattened[j] * WeightsInputHidden
[j, i];
384 }
385 hiddenOutput[i] += BiasHidden[i];
386 hiddenOutput[i] = Math.Tanh(hiddenOutput[i]); // Apply
activation
387 }
388
389 double[] output = new double[NumClasses];
390 for (int i = 0; i < NumClasses; i++)
391 {
392 for (int j = 0; j < HiddenSize; j++)
393 {
394 output[i] += hiddenOutput[j] * WeightsHiddenOutput[j,
i];
395 }
396 output[i] += BiasOutput[i];
397 }
398
399 return (hiddenOutput, Softmax(output)); // Return both
hiddenOutput and output
400 }
...os\ImageClassification\ImageClassification\Program.cs 10
401 static double[,] ConvertTo2D(double[] input)
402 {
403 int imageSize = 32; // CIFAR-10 images are 32x32
404 double[,] output = new double[imageSize, imageSize];
405
406 for (int i = 0; i < imageSize; i++)
407 {
408 for (int j = 0; j < imageSize; j++)
409 {
410 output[i, j] = input[i * imageSize + j];
411 }
412 }
413
414 return output;
415 }
416 #endregion
417
418 }
419 }