Skip to content

Commit deee8a0

Browse files
feat: add a loss history bar graph
1 parent 9954791 commit deee8a0

2 files changed

Lines changed: 154 additions & 2 deletions

File tree

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,24 @@
11
import { MLP } from "./NeuralNet";
2+
import { verticalBarGraph } from "./utils";
23

34
let model = new MLP(10, [23, 443, 12], 3);
45

5-
let output = model.forward_propogation([23, 43, 23, 12, 4, 54, 23, 43, 12, 3]);
6-
console.log(output);
6+
let old_output = model.forward_propogation([
7+
23, 43, 23, 12, 4, 54, 23, 43, 12, 3,
8+
]);
9+
10+
const history: number[] = [model.loss([0, 1, 0])];
11+
for (let index = 0; index < 1000; index++) {
12+
model.backpropogate([23, 43, 23, 12, 4, 54, 23, 43, 12, 3], [0, 1, 0], 0.03);
13+
history.push(model.loss([0, 1, 0]));
14+
}
15+
let new_output = model.forward_propogation([
16+
23, 43, 23, 12, 4, 54, 23, 43, 12, 3,
17+
]);
18+
let new_loss = model.loss([0, 1, 0]);
19+
20+
console.log(old_output);
21+
console.log(new_output);
22+
console.log(history);
23+
24+
verticalBarGraph(history);
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,137 @@
11
export function sigmoid(x: number) {
22
return 1 / (1 + Math.exp(-x));
33
}
4+
/**
5+
* Display a vertical bar graph in the terminal for large datasets
6+
* @param data Array of numbers to display
7+
* @param options Configuration options for the graph
8+
*/
9+
export function verticalBarGraph(
10+
data: number[],
11+
options: {
12+
title?: string;
13+
barChar?: string;
14+
maxHeight?: number;
15+
maxBars?: number;
16+
startIndex?: number;
17+
showLabels?: boolean;
18+
showValues?: boolean;
19+
sampling?: "none" | "every-n" | "average";
20+
samplingFactor?: number;
21+
} = {},
22+
): void {
23+
const {
24+
title = "Data Visualization",
25+
barChar = "█",
26+
maxHeight = 20,
27+
maxBars = getTerminalWidth() - 10,
28+
startIndex = 0,
29+
showLabels = true,
30+
showValues = true,
31+
sampling = "none",
32+
samplingFactor = 1,
33+
} = options;
34+
35+
function getTerminalWidth(): number {
36+
return process.stdout.columns || 80;
37+
}
38+
39+
console.log(`\n${title}`);
40+
41+
if (data.length === 0) {
42+
console.log("No data to display");
43+
return;
44+
}
45+
46+
// Apply sampling if needed
47+
let displayData: number[] = [];
48+
let originalIndices: number[] = [];
49+
50+
if (sampling === "none") {
51+
const endIndex = Math.min(startIndex + maxBars, data.length);
52+
displayData = data.slice(startIndex, endIndex);
53+
originalIndices = Array.from(
54+
{ length: displayData.length },
55+
(_, i) => i + startIndex,
56+
);
57+
} else if (sampling === "every-n") {
58+
for (let i = 0; i < data.length; i += samplingFactor) {
59+
if (displayData.length >= maxBars) break;
60+
displayData.push(data[i]);
61+
originalIndices.push(i);
62+
}
63+
} else if (sampling === "average") {
64+
for (let i = 0; i < data.length; i += samplingFactor) {
65+
if (displayData.length >= maxBars) break;
66+
const chunk = data.slice(i, i + samplingFactor);
67+
const avg = chunk.reduce((sum, val) => sum + val, 0) / chunk.length;
68+
displayData.push(avg);
69+
originalIndices.push(i);
70+
}
71+
}
72+
73+
// Calculate the maximum value for scaling
74+
const maxValue = Math.max(...displayData);
75+
76+
// Create the vertical bars
77+
const scaledData = displayData.map((value) =>
78+
Math.max(1, Math.round((value / maxValue) * maxHeight)),
79+
);
80+
81+
// Build the chart from top to bottom
82+
const chart: string[][] = [];
83+
84+
// Add values at the top if requested
85+
if (showValues) {
86+
const valueRow: string[] = [];
87+
for (let i = 0; i < displayData.length; i++) {
88+
// Truncate value to fit in column
89+
const value = displayData[i].toString().slice(0, 3);
90+
valueRow.push(value.padStart(1).padEnd(1));
91+
}
92+
chart.push(valueRow);
93+
}
94+
95+
// Build the bars
96+
for (let row = 0; row < maxHeight; row++) {
97+
const chartRow: string[] = [];
98+
for (let col = 0; col < scaledData.length; col++) {
99+
// If current height is within bar height, add bar character
100+
chartRow.push(maxHeight - row <= scaledData[col] ? barChar : " ");
101+
}
102+
chart.push(chartRow);
103+
}
104+
105+
// Add index labels at the bottom if requested
106+
if (showLabels) {
107+
const labelRow: string[] = [];
108+
for (let i = 0; i < originalIndices.length; i++) {
109+
// Add label every 5 indices to avoid crowding
110+
const index = originalIndices[i];
111+
const label = i % 5 === 0 ? index.toString().slice(-1) : "";
112+
labelRow.push(label.padStart(1));
113+
}
114+
chart.push(labelRow);
115+
}
116+
117+
// Render the chart
118+
for (const row of chart) {
119+
console.log(row.join(""));
120+
}
121+
122+
// Show summary of what's displayed
123+
if (data.length > displayData.length) {
124+
console.log(`\nShowing ${displayData.length} of ${data.length} items`);
125+
if (sampling !== "none") {
126+
console.log(`Sampling: ${sampling} with factor ${samplingFactor}`);
127+
} else {
128+
console.log(
129+
`Range: ${startIndex + 1}-${startIndex + displayData.length}`,
130+
);
131+
}
132+
}
133+
134+
const sum = displayData.reduce((acc, val) => acc + val, 0);
135+
const avg = sum / displayData.length;
136+
console.log(`\nAvg: ${avg.toFixed(2)} | Max: ${maxValue.toFixed(2)}`);
137+
}

0 commit comments

Comments
 (0)