alexandrebrillant_perceptron 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +153 -0
- package/dist/perceptron.min.mjs +1 -0
- package/dist/perceptron.mjs +110 -0
- package/doc/result.png +0 -0
- package/package.json +36 -0
- package/src/perceptron.mjs +110 -0
- package/test/test.html +118 -0
- package/test/test.js +55 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Alexandre Brillant
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Perceptron
|
|
2
|
+
|
|
3
|
+
A Simple JavaScript Implementation of the Perceptron AI Algorithm
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
Add any sample using the **addTrainingSample**. A Sample is a set of values with a label. You can have only two labels. A label is a free string.
|
|
8
|
+
|
|
9
|
+
Calling **train** will update the inner weights of the perceptron. You can specify
|
|
10
|
+
too a max training size. It return an error rate. 1 (100%) means it can't train the samples and 0 (0%) means this is perfect.
|
|
11
|
+
|
|
12
|
+
After training the perceptron, you can use the **predict** method with a set of values. It will return the label for this values.
|
|
13
|
+
|
|
14
|
+
In the following sample, we have two labels: green and red. Green represents a negative value, and red represents a positive one. We let the perceptron find the correct weights during training to predict them.
|
|
15
|
+
|
|
16
|
+

|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
|
|
20
|
+
import { Perceptron } from "../dist/perceptron.mjs";
|
|
21
|
+
|
|
22
|
+
const perceptron = new Perceptron();
|
|
23
|
+
|
|
24
|
+
const sample1 = {
|
|
25
|
+
sample: [],
|
|
26
|
+
label:"red"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
for ( let sample = 0; sample < 100; sample++ )
|
|
30
|
+
sample1.sample.push( Math.random() * 100 );
|
|
31
|
+
|
|
32
|
+
const sample2 = {
|
|
33
|
+
sample : [],
|
|
34
|
+
label:"green"
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
for ( let sample = 0; sample < 100; sample++ )
|
|
38
|
+
sample2.sample.push( -Math.random() * 100 );
|
|
39
|
+
|
|
40
|
+
perceptron.addTrainingSample( sample1 );
|
|
41
|
+
perceptron.addTrainingSample( sample2 );
|
|
42
|
+
|
|
43
|
+
const errorRate = perceptron.train();
|
|
44
|
+
|
|
45
|
+
console.log( "Training good result rate " + ( 100 - errorRate ) + "%" );
|
|
46
|
+
|
|
47
|
+
console.log( perceptron.predict( [-10,-2] ) ); // green
|
|
48
|
+
|
|
49
|
+
console.log( perceptron.predict( [200,30] ) ); // red
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Draw the result separator
|
|
53
|
+
|
|
54
|
+
In this example, we have two sets of points. We display the points and the separator line found by the perceptron.
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
```html
|
|
58
|
+
<!DOCTYPE html>
|
|
59
|
+
<html>
|
|
60
|
+
<head>
|
|
61
|
+
|
|
62
|
+
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
|
|
63
|
+
|
|
64
|
+
<script type="module">
|
|
65
|
+
|
|
66
|
+
import { Perceptron } from "../dist/perceptron.mjs";
|
|
67
|
+
|
|
68
|
+
const perceptron = new Perceptron();
|
|
69
|
+
|
|
70
|
+
const trace1 = {
|
|
71
|
+
x:[],
|
|
72
|
+
y:[],
|
|
73
|
+
mode:'markers',
|
|
74
|
+
type:'scatter'
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const trace2 = {
|
|
78
|
+
x:[],
|
|
79
|
+
y:[],
|
|
80
|
+
mode:'markers',
|
|
81
|
+
type:'scatter'
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
for ( let sample = 0; sample < 100; sample++ ) {
|
|
85
|
+
|
|
86
|
+
let x1,y1;
|
|
87
|
+
let x2,y2;
|
|
88
|
+
|
|
89
|
+
const sample1 = {
|
|
90
|
+
sample: [ x1 = ( Math.random() * 100 ) + 20, y1 = ( Math.random() * 100 ) + 20 ],
|
|
91
|
+
label:"red"
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const sample2 = {
|
|
95
|
+
sample: [ x2 = ( Math.random() * 100 ) - 50, y2 = ( Math.random() * 100 ) - 50 ],
|
|
96
|
+
label : "green"
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
trace1.x.push( x1 );
|
|
100
|
+
trace1.y.push( y1 );
|
|
101
|
+
|
|
102
|
+
trace2.x.push( x2 );
|
|
103
|
+
trace2.y.push( y2 );
|
|
104
|
+
|
|
105
|
+
perceptron.addTrainingSample( sample1 );
|
|
106
|
+
perceptron.addTrainingSample( sample2 );
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const errorRate = perceptron.train();
|
|
110
|
+
|
|
111
|
+
const a1 = -50;
|
|
112
|
+
const a2 = 120;
|
|
113
|
+
const w1 = perceptron.weight( 0 );
|
|
114
|
+
const w2 = perceptron.weight( 1 );
|
|
115
|
+
|
|
116
|
+
const b = perceptron.biais();
|
|
117
|
+
|
|
118
|
+
const b1 = (-w1 * a1 - b) / w2;
|
|
119
|
+
const b2 = (-w1 * a2 - b) / w2;
|
|
120
|
+
|
|
121
|
+
const trace3 =
|
|
122
|
+
{
|
|
123
|
+
x: [a1, a2],
|
|
124
|
+
y: [b1, b2],
|
|
125
|
+
mode: 'lines',
|
|
126
|
+
type: 'scatter',
|
|
127
|
+
name: 'Separator',
|
|
128
|
+
line: { color: 'blue', dash: 'dash' }
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
let data = [ trace1, trace2, trace3 ];
|
|
132
|
+
|
|
133
|
+
Plotly.newPlot( 'plots', data );
|
|
134
|
+
|
|
135
|
+
</script>
|
|
136
|
+
</head>
|
|
137
|
+
|
|
138
|
+
<body>
|
|
139
|
+
|
|
140
|
+
<h1>Perceptron result</h1>
|
|
141
|
+
|
|
142
|
+
<div id="plots">
|
|
143
|
+
|
|
144
|
+
</div>
|
|
145
|
+
</body>
|
|
146
|
+
|
|
147
|
+
</html>
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
(c) 2026 Alexandre Brillant
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export class Perceptron{#samples=[];#weights=null;#biais=0;scalarProduct(sample){let scalar=this.#biais;for(let i=0;i<sample.length;i++){scalar+=sample[i]*this.#weights[i]}return scalar}#outputs={};#output=-1;#labels=[];weight(index){return this.#weights[index]}biais(){return this.#biais}addTrainingSample({sample:sample,label:label}){if(!Array.isArray(sample))throw"Invalid sample, must be an Array";if(this.#samples.length>0){const previousLength=this.#samples.at(-1).sample.length;if(previousLength!=sample.length)throw"Invalid sample size, must have a x"+previousLength+" size"}this.#weights=this.#weights??new Array(sample.length).fill(0);if(!(label in this.#outputs)){this.#outputs[label]=this.#output;this.#labels.push(label);this.#output+=2;if(this.#output>4)throw"Illegal label, must have only 2 labels"}this.#samples.push({sample:sample,output:this.#outputs[label]})}#updateWeights({sample:sample,output:output}){this.#biais+=output;for(let i=0;i<sample.length;i++){this.#weights[i]+=output*sample[i]}}train(trainingSize=100){if(this.#samples.length==0)throw"No training data ?";let errorFound=false;let totalError=0;do{errorFound=false;totalError=0;this.#samples.forEach(({sample:sample,output:output})=>{if(output*this.scalarProduct(sample)<=0){errorFound=true;totalError++;this.#updateWeights({sample:sample,output:output})}});trainingSize--}while(trainingSize>0&&errorFound);return totalError/this.#samples.length}predict(sample){const result=this.scalarProduct(sample);if(result>=0)return this.#labels[1];return this.#labels[0]}}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Perceptron.mjs
|
|
3
|
+
* Copyright 2026 Alexandre Brillant
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
export class Perceptron {
|
|
27
|
+
|
|
28
|
+
#samples = [];
|
|
29
|
+
#weights = null;
|
|
30
|
+
#biais = 0;
|
|
31
|
+
|
|
32
|
+
// W*X
|
|
33
|
+
scalarProduct( sample ) {
|
|
34
|
+
let scalar = this.#biais;
|
|
35
|
+
for ( let i = 0; i < sample.length; i++ ) {
|
|
36
|
+
scalar += sample[ i ] * this.#weights[ i ];
|
|
37
|
+
}
|
|
38
|
+
return scalar;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
#outputs = {};
|
|
42
|
+
#output = -1;
|
|
43
|
+
#labels = [];
|
|
44
|
+
|
|
45
|
+
weight( index ) {
|
|
46
|
+
return this.#weights[ index ];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
biais() {
|
|
50
|
+
return this.#biais;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
addTrainingSample( {sample, label} ) {
|
|
54
|
+
if ( !Array.isArray( sample ) )
|
|
55
|
+
throw "Invalid sample, must be an Array";
|
|
56
|
+
|
|
57
|
+
if ( this.#samples.length > 0 ) {
|
|
58
|
+
const previousLength = this.#samples.at( -1 ).sample.length;
|
|
59
|
+
if ( previousLength != sample.length )
|
|
60
|
+
throw "Invalid sample size, must have a x" + previousLength + " size";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.#weights = this.#weights ?? new Array( sample.length ).fill( 0.0 );
|
|
64
|
+
|
|
65
|
+
if ( !( label in this.#outputs ) ) {
|
|
66
|
+
this.#outputs[ label ] = this.#output;
|
|
67
|
+
this.#labels.push( label );
|
|
68
|
+
this.#output += 2;
|
|
69
|
+
if ( this.#output > 4 )
|
|
70
|
+
throw "Illegal label, must have only 2 labels"
|
|
71
|
+
}
|
|
72
|
+
this.#samples.push( { sample, output:this.#outputs[ label ] } );
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#updateWeights( { sample, output } ) {
|
|
76
|
+
this.#biais += output;
|
|
77
|
+
for ( let i = 0; i < sample.length; i++ ) {
|
|
78
|
+
this.#weights[ i ] += output * sample[ i ];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
train( trainingSize = 100 ) {
|
|
83
|
+
if ( this.#samples.length == 0 )
|
|
84
|
+
throw "No training data ?";
|
|
85
|
+
let errorFound = false;
|
|
86
|
+
let totalError = 0;
|
|
87
|
+
do {
|
|
88
|
+
errorFound = false;
|
|
89
|
+
totalError = 0;
|
|
90
|
+
this.#samples.forEach(
|
|
91
|
+
( { sample, output } ) => {
|
|
92
|
+
if ( output * this.scalarProduct( sample ) <= 0 ) {
|
|
93
|
+
errorFound = true;
|
|
94
|
+
totalError++;
|
|
95
|
+
this.#updateWeights( { sample, output } );
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
trainingSize--;
|
|
100
|
+
} while ( trainingSize > 0 && errorFound );
|
|
101
|
+
return totalError / this.#samples.length;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
predict( sample ) {
|
|
105
|
+
const result = this.scalarProduct( sample );
|
|
106
|
+
if ( result >= 0 ) return this.#labels[ 1 ];
|
|
107
|
+
return this.#labels[ 0 ];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
}
|
package/doc/result.png
ADDED
|
Binary file
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "alexandrebrillant_perceptron",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A Simple JavaScript Implementation of the Perceptron Algorithm",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"perceptron",
|
|
7
|
+
"percepton javascript",
|
|
8
|
+
"artificial neuron",
|
|
9
|
+
"supervised learning",
|
|
10
|
+
"neuron",
|
|
11
|
+
"ia",
|
|
12
|
+
"ai"
|
|
13
|
+
],
|
|
14
|
+
"homepage": "https://github.com/AlexandreBrillant/perceptron#readme",
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/AlexandreBrillant/perceptron/issues"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/AlexandreBrillant/perceptron.git"
|
|
21
|
+
},
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"author": "Alexandre Brillant",
|
|
24
|
+
"type": "module",
|
|
25
|
+
"main": "perceptron.mjs",
|
|
26
|
+
"scripts": {
|
|
27
|
+
"test": "node test/test.js",
|
|
28
|
+
"predeploy": "node --check src/perceptron.mjs",
|
|
29
|
+
"deploy": "cp src/perceptron.mjs dist",
|
|
30
|
+
"postdeploy": "npx terser dist/perceptron.mjs > dist/perceptron.min.mjs"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"http-server": "^14.1.1",
|
|
34
|
+
"terser": "^5.46.1"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Perceptron.mjs
|
|
3
|
+
* Copyright 2026 Alexandre Brillant
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
export class Perceptron {
|
|
27
|
+
|
|
28
|
+
#samples = [];
|
|
29
|
+
#weights = null;
|
|
30
|
+
#biais = 0;
|
|
31
|
+
|
|
32
|
+
// W*X
|
|
33
|
+
scalarProduct( sample ) {
|
|
34
|
+
let scalar = this.#biais;
|
|
35
|
+
for ( let i = 0; i < sample.length; i++ ) {
|
|
36
|
+
scalar += sample[ i ] * this.#weights[ i ];
|
|
37
|
+
}
|
|
38
|
+
return scalar;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
#outputs = {};
|
|
42
|
+
#output = -1;
|
|
43
|
+
#labels = [];
|
|
44
|
+
|
|
45
|
+
weight( index ) {
|
|
46
|
+
return this.#weights[ index ];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
biais() {
|
|
50
|
+
return this.#biais;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
addTrainingSample( {sample, label} ) {
|
|
54
|
+
if ( !Array.isArray( sample ) )
|
|
55
|
+
throw "Invalid sample, must be an Array";
|
|
56
|
+
|
|
57
|
+
if ( this.#samples.length > 0 ) {
|
|
58
|
+
const previousLength = this.#samples.at( -1 ).sample.length;
|
|
59
|
+
if ( previousLength != sample.length )
|
|
60
|
+
throw "Invalid sample size, must have a x" + previousLength + " size";
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.#weights = this.#weights ?? new Array( sample.length ).fill( 0.0 );
|
|
64
|
+
|
|
65
|
+
if ( !( label in this.#outputs ) ) {
|
|
66
|
+
this.#outputs[ label ] = this.#output;
|
|
67
|
+
this.#labels.push( label );
|
|
68
|
+
this.#output += 2;
|
|
69
|
+
if ( this.#output > 4 )
|
|
70
|
+
throw "Illegal label, must have only 2 labels"
|
|
71
|
+
}
|
|
72
|
+
this.#samples.push( { sample, output:this.#outputs[ label ] } );
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#updateWeights( { sample, output } ) {
|
|
76
|
+
this.#biais += output;
|
|
77
|
+
for ( let i = 0; i < sample.length; i++ ) {
|
|
78
|
+
this.#weights[ i ] += output * sample[ i ];
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
train( trainingSize = 100 ) {
|
|
83
|
+
if ( this.#samples.length == 0 )
|
|
84
|
+
throw "No training data ?";
|
|
85
|
+
let errorFound = false;
|
|
86
|
+
let totalError = 0;
|
|
87
|
+
do {
|
|
88
|
+
errorFound = false;
|
|
89
|
+
totalError = 0;
|
|
90
|
+
this.#samples.forEach(
|
|
91
|
+
( { sample, output } ) => {
|
|
92
|
+
if ( output * this.scalarProduct( sample ) <= 0 ) {
|
|
93
|
+
errorFound = true;
|
|
94
|
+
totalError++;
|
|
95
|
+
this.#updateWeights( { sample, output } );
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
);
|
|
99
|
+
trainingSize--;
|
|
100
|
+
} while ( trainingSize > 0 && errorFound );
|
|
101
|
+
return totalError / this.#samples.length;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
predict( sample ) {
|
|
105
|
+
const result = this.scalarProduct( sample );
|
|
106
|
+
if ( result >= 0 ) return this.#labels[ 1 ];
|
|
107
|
+
return this.#labels[ 0 ];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
}
|
package/test/test.html
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html>
|
|
3
|
+
<head>
|
|
4
|
+
|
|
5
|
+
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
|
|
6
|
+
|
|
7
|
+
<script type="module">
|
|
8
|
+
|
|
9
|
+
/*
|
|
10
|
+
* test.html
|
|
11
|
+
*
|
|
12
|
+
* Draw points and the result line of the perceptron
|
|
13
|
+
*
|
|
14
|
+
* Copyright 2026 Alexandre Brillant
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
19
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
20
|
+
in the Software without restriction, including without limitation the rights
|
|
21
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
22
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
23
|
+
furnished to do so, subject to the following conditions:
|
|
24
|
+
|
|
25
|
+
The above copyright notice and this permission notice shall be included in all
|
|
26
|
+
copies or substantial portions of the Software.
|
|
27
|
+
|
|
28
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
29
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
30
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
31
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
32
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
33
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
34
|
+
SOFTWARE.
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
import { Perceptron } from "../dist/perceptron.mjs";
|
|
38
|
+
|
|
39
|
+
const perceptron = new Perceptron();
|
|
40
|
+
|
|
41
|
+
const trace1 = {
|
|
42
|
+
x:[],
|
|
43
|
+
y:[],
|
|
44
|
+
mode:'markers',
|
|
45
|
+
type:'scatter'
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const trace2 = {
|
|
49
|
+
x:[],
|
|
50
|
+
y:[],
|
|
51
|
+
mode:'markers',
|
|
52
|
+
type:'scatter'
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
for ( let sample = 0; sample < 100; sample++ ) {
|
|
56
|
+
|
|
57
|
+
let x1,y1;
|
|
58
|
+
let x2,y2;
|
|
59
|
+
|
|
60
|
+
const sample1 = {
|
|
61
|
+
sample: [ x1 = ( Math.random() * 100 ) + 20, y1 = ( Math.random() * 100 ) + 20 ],
|
|
62
|
+
label:"red"
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const sample2 = {
|
|
66
|
+
sample: [ x2 = ( Math.random() * 100 ) - 50, y2 = ( Math.random() * 100 ) - 50 ],
|
|
67
|
+
label : "green"
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
trace1.x.push( x1 );
|
|
71
|
+
trace1.y.push( y1 );
|
|
72
|
+
|
|
73
|
+
trace2.x.push( x2 );
|
|
74
|
+
trace2.y.push( y2 );
|
|
75
|
+
|
|
76
|
+
perceptron.addTrainingSample( sample1 );
|
|
77
|
+
perceptron.addTrainingSample( sample2 );
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const errorRate = perceptron.train();
|
|
81
|
+
|
|
82
|
+
const a1 = -50;
|
|
83
|
+
const a2 = 120;
|
|
84
|
+
const w1 = perceptron.weight( 0 );
|
|
85
|
+
const w2 = perceptron.weight( 1 );
|
|
86
|
+
|
|
87
|
+
const b = perceptron.biais();
|
|
88
|
+
|
|
89
|
+
const b1 = (-w1 * a1 - b) / w2;
|
|
90
|
+
const b2 = (-w1 * a2 - b) / w2;
|
|
91
|
+
|
|
92
|
+
const trace3 =
|
|
93
|
+
{
|
|
94
|
+
x: [a1, a2],
|
|
95
|
+
y: [b1, b2],
|
|
96
|
+
mode: 'lines',
|
|
97
|
+
type: 'scatter',
|
|
98
|
+
name: 'Separator',
|
|
99
|
+
line: { color: 'blue', dash: 'dash' }
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
let data = [ trace1, trace2, trace3 ];
|
|
103
|
+
|
|
104
|
+
Plotly.newPlot( 'plots', data );
|
|
105
|
+
|
|
106
|
+
</script>
|
|
107
|
+
</head>
|
|
108
|
+
|
|
109
|
+
<body>
|
|
110
|
+
|
|
111
|
+
<h1>Perceptron result</h1>
|
|
112
|
+
|
|
113
|
+
<div id="plots">
|
|
114
|
+
|
|
115
|
+
</div>
|
|
116
|
+
</body>
|
|
117
|
+
|
|
118
|
+
</html>
|
package/test/test.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* test.js
|
|
3
|
+
* Copyright 2026 Alexandre Brillant
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/*
|
|
7
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
9
|
+
in the Software without restriction, including without limitation the rights
|
|
10
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
11
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
12
|
+
furnished to do so, subject to the following conditions:
|
|
13
|
+
|
|
14
|
+
The above copyright notice and this permission notice shall be included in all
|
|
15
|
+
copies or substantial portions of the Software.
|
|
16
|
+
|
|
17
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
18
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
19
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
20
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
21
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
22
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
23
|
+
SOFTWARE.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { Perceptron } from "../dist/perceptron.mjs";
|
|
27
|
+
|
|
28
|
+
const perceptron = new Perceptron();
|
|
29
|
+
|
|
30
|
+
const sample1 = {
|
|
31
|
+
sample: [],
|
|
32
|
+
label:"red"
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
for ( let sample = 0; sample < 100; sample++ )
|
|
36
|
+
sample1.sample.push( Math.random() * 100 );
|
|
37
|
+
|
|
38
|
+
const sample2 = {
|
|
39
|
+
sample : [],
|
|
40
|
+
label:"green"
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
for ( let sample = 0; sample < 100; sample++ )
|
|
44
|
+
sample2.sample.push( -Math.random() * 100 );
|
|
45
|
+
|
|
46
|
+
perceptron.addTrainingSample( sample1 );
|
|
47
|
+
perceptron.addTrainingSample( sample2 );
|
|
48
|
+
|
|
49
|
+
const errorRate = perceptron.train();
|
|
50
|
+
|
|
51
|
+
console.log( "Training good result rate " + ( 100 - errorRate ) + "%" );
|
|
52
|
+
|
|
53
|
+
console.log( perceptron.predict( [-10,-2] ) ); // green
|
|
54
|
+
|
|
55
|
+
console.log( perceptron.predict( [200,30] ) ); // red
|