Prädiktion von Materialeigenschaften - Eine Einführung (Stefan Bosse) [2.2021] |
[1] M. A. Devi, C. P. S. Prakash, R. P. Chinnannavar, V. P. Joshi, R. S. Palada, and R. Dixit, “An Informatic Approach to Predict the Mechanical Properties of Aluminum Alloys using Machine Learning Techniques,” in Proceedings of the International Conference on Smart Electronics and Communication (ICOSEC 2020) IEEE Xplore Part Number: CFP20V90-ART, 2020. PDF
[2] C. Karina, P.-jo Chun, and K. Okubo, “Tensile strength prediction of corroded steel plates by using machine learning approach,” Steel and Composite Structures, vol. 24, no. 5, 2017. PDF
[3] S. Bosse, E. Kalwait, Damage and Material-state Diagnostics with Predictor Functions using Data Series Prediction and Artificial Neural Networks, ECSA 2020 MDPI, 15.11 -30.11.2020, Basel, Switzerland PDF
DATA: Variable State.dataSets Type: (number [2][874]|number [2][759]|number [2][872]|number [2][808]|number [2][710]|number [2][788]|number [2][731]|number [2][728]|number [2][725]|number [2][785]|number [2][713]|number [2][803]|number [2][575]|number [2][536]|number [2][585]|number [2][1110]|number [2][1185]|number [2][988]|number [2][1124]|number [2][1009]|number [2][1143]|number [2][1080]|number [2][1107]|number [2][761]|number [2][780]|number [2][779]|number [2][689]|number [2][836]|number [2][739]|number [2][789]|number [2][747]|number [2][592]|number [2][802]|number [2][729]|number [2][712]|number [2][660]|number [2][796])[42]
typeOf dataSets = number [strain,force] [rows] [42]
1
2
3
4
var sample=0;
Plot({x:dataSets[sample].pluck(0).sample(5),
y:dataSets[sample].pluck(1).sample(5)},
{type:'line',labels:{x:'strain [mm]',y:'force [N]'}})
▸
|
✗
≡
|
Eingabevariablen sind der gemessene Weg (Dehnung) x und die auftretendene gemessene Kraft f. Obwohl hier y=f(x) gilt, ist f nicht die Zielvariable sondern die zweite Eingabevariable!
Die Zielvariable ergibt sich aus der Fragestellung und soll der x Wert sein wo es zu dem Bruch der Probe komnt (siehe Messkurve), d.h., y=xb einer Messrreihe und einer Probe.
Für das Training muss der Werte xb aus jeder Messreiehe approximativ berechnet werden (Das Label für das ML Training)
Das algorithmisch zu lernende Modell soll dabei die ersten 10 Messpunkte (x,f) als Eingabeparameter und den prädiktiven xb Wert als Ausgangsparameter besitzen:
Die Messreihe liegt aber nicht linear mit Bezug zu der x Variable vor, d.h. Δx≠const!
Daher müssen die Daten linearsiert werden (nicht zu verwechseln mit der Normalisierung/Skalierung, die auch nich erfolgen muss)
1
2
3
4
5
6
7
8
9
var self=this;
this.data0=dataSets.map(function (data,index) {
var off=0;
while (data[off][0]<0) off++;
print('#'+index+' starting at row '+off);
if (off>0) data=data.slice(off,null,true);
return Math.Matrix(data);
});
print('Done.')
▸
|
✗
≡
|
Die experimentellen Daten bestehen aus einer unterschiedlichen Anzahl von Datenpunkten (x,f).
Dabei gehören immer ein (x,f) Tupel zusammen.
Die Linearisierung tastet die (x,f) Datenpaaren mit Δx=x1 ab.
Da dann die lineariserten x Werte keine Information mehr tragen (außer einer inherenten Ordnungsrelation) können diese entfernt und nur noch die zugehörigen f Werte verwendet werden.
Als Wertebereich für die ausgewählten linearisierten Samples einer Datenreihe wird [*x*~0~,*x*~1~]=[0,0.1] verwendet (bei den meisten Kurven überwiegend noch der linear-elastische Bereich)
Die Labels (also xd Punkte) werden über eine Differenzierung ermittelt:
1
2
3
4
5
6
7
8
// calculate damage point (x value)
var self=this;
this.labels=this.data0.map(function (sample) {
return sample.col(0)[
sample.col(1,true).derivative().min(true)
]
});
Plot(this.labels,{type:'bar',color:'#888',min:0,max:1.5})
▸
|
✗
≡
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
var dataXY = [], dataIO=[];
this.config = {
// Sample range x={x0,x0+d,x0+2d,..,x1}
x0: 0,
x1: 0.1,
xdelta:0.01,
// Datensatzpartitionen Train/Test
part1A: 21, part1B: 21,
// Skalierungsdeskriptoren für die Kraftvariable und die Wegavriable
scaleF : ML.toScale(0,10000),
scaleX : ML.toScale(0,2)
}
for(var i in this.data0) {
var x=this.data0[i].pluck(0).sample(5),
y=this.data0[i].pluck(1).sample(5),
input=[],output;
var next=this.config.xdelta,delta=this.config.xdelta;
// Linearisierung und Normalisierung der y-sample Vektoren nach x
for (var xi=0;xi<x.length;xi++) {
if (next>this.config.x1) break;
if (Math.abs(x.get(xi)-next)<this.config.xdelta*2) {
// take sample
input.push(y.get(xi));
next += delta;
}
}
// wird für ML.ML.MLP benötigt
dataXY.push({
x : ML.scale(input,this.config.scaleF),
y : ML.scale([this.labels[i]],this.config.scaleX).wrap(),
})
// wird für ML.ML.ANN benötigt
dataIO.push({
input : ML.scale(input,this.config.scaleF),
output : ML.scale([this.labels[i]],this.config.scaleX).wrap(),
})
}
this.dataXY=dataXY;
this.dataIO=dataIO;
▸
|
✗
≡
|
Es wird Monte Carlo Simulation mit haußverteilten Rauschen eingesetzt um die Varianz der Dateninstanzen zu vergrößern
Der Parameter ε bestimmt die Bandbreite des Rauschens (da x/y auf [0,1] normiert sind bedeutet 0.1 → 10% Rauschen)
1
2
3
4
5
6
7
8
9
10
11
var dataXYR=[];
var eps = 0.1
for(var i in this.dataXY) {
var row=this.dataXY[i];
for (var n=0;n<3;n++) dataXYR.push({
x: ML.noise(row.x,eps),
y: ML.noise(row.y,eps)
})
}
this.dataXYR=dataXYR;
▸
|
✗
≡
|
Aufgrund der kleinen Datensatzmenge und Varianz ist die strikte Aufteilung in Trainings- und Testdatensample hier besonders kritisch
Zuerst mit allen Dateninstanzen trainieren und Fehlerbetrachtung durchführen (s.u.).
1
2
3
4
var parts = ML.split(this.dataIO,40,2);
this.dataTraining = parts[0];
this.dataTest = parts[1];
▸
|
✗
≡
|
1
2
3
4
5
6
7
8
9
10
11
12
//
//if (this.modelSaved)
// that.modelA = ML.learner({
// algorithm:ML.ML.ANN,
// network: this.modelSaved,
// });
//else
that.modelA = ML.learner({
algorithm:ML.ML.ANN,
layers: [10,2,1],
verbose : 1,
});
▸
|
✗
≡
|
Serialisierung des Modells (nur zum Speichern ausführen)Das Modell ist nicht direkt speicherbar!! Eine explizite Serialisierung und Deserialisierung wäre erforderlich. nache einem erneuten Laden der notebook JSON Datei ist das Modell in this.modelA ungültig!
1
this.modelSaved=that.modelA.network.toJSON();
▸
|
✗
≡
|
1
2
3
4
5
6
7
8
9
10
11
that.modelB = ML.learner({
algorithm:ML.ML.SVM,
x:this.dataXY.pluck('x'),
y:this.dataXY.pluck('y'),
threshold : false,
C : 1.0, // default : 1.0. C in SVM.
tol : 1e-5, // default : 1e-4. Higher tolerance --> Higher precision
max_passes : 200, // default : 20. Higher max_passes --> Higher precision
alpha_tol : 1e-5, // default : 1e-5. Higher alpha_tolerance --> Higher precision
kernel : { type: 'rbf', sigma: 2.5 } // { type: "polynomial", c: 1, d: 5}
});
▸
|
✗
≡
|
Training bedeutet hier nur die Erstellung eines Graphennetzwerkes mit und aus allen Dateninstanzen!
Der KNN Lerner erwartet Matrixtabellen, daher ist eine Zusammenführung von x und y Spalten erforderlich
1
2
3
4
5
6
7
8
9
10
// hier dataXY/dataXYR usw ersetzen
var data = this.dataXY.pluck('x')
.merge(this.dataXY.pluck('y'),'c');
that.modelC = ML.learner({
algorithm:ML.ML.KNN,
data:data,
features : Array.sequence(0,9),
target : 10
});
▸
|
✗
≡
|
1
2
3
4
5
6
7
8
ML.log(print);
ML.train(that.modelA,
this.dataIO,
{
rate:0.2,
iterations:1000,
verbose:1,
});
▸
|
✗
≡
|
1
2
3
4
5
6
7
8
9
10
var dataY0=this.dataXY.pluck('y');
var results=ML.predict(that.modelA,this.dataXY.pluck('x'));
var errorTotal=0,errorMax=0;
Table(results.merge(dataY0,'c').merge(results.map(function (y,i) {
var error=Math.abs(y-dataY0[i])/dataY0[i]*100;
errorTotal += error;
errorMax=Math.max(errorMax,error);
return error;
}),'c'));
print(errorTotal/results.length,errorMax);
▸
|
✗
≡
|
1
2
3
4
5
6
7
8
9
10
var dataY0=this.dataXY.pluck('y');
var results=ML.predict(that.modelB,this.dataXY.pluck('x'));
var errorTotal=0,errorMax=0;
Table(results.merge(dataY0,'c').merge(results.map(function (y,i) {
var error=Math.abs(y-dataY0[i])/dataY0[i]*100;
errorTotal += error;
errorMax=Math.max(errorMax,error);
return error;
}),'c'));
print(errorTotal/results.length,errorMax);
▸
|
✗
≡
|
1
2
3
4
5
6
7
8
9
10
var dataY0=this.dataXY.pluck('y');
var results=ML.classify(that.modelC,this.dataXY.pluck('x'));
var errorTotal=0,errorMax=0;
Table(results.merge(dataY0,'c').merge(results.map(function (y,i) {
var error=Math.abs(y-dataY0[i])/dataY0[i]*100;
errorTotal += error;
errorMax=Math.max(errorMax,error);
return error;
}),'c'));
print(errorTotal/results.length,errorMax);
▸
|
✗
≡
|
Zunächst Training mit allen Dateninstanzen durchführen (Modell A)
Finde heraus ob das Problem mit einem ANN ohne innere Schichten mit einem maximalen Fehler von höchstens 30% lösbar ist
Füge innere Schichten hinzu mit unterschiedlicher Anzahl von Neuronen. Gibt es eine Verbesserung? Achtung: Insgesamt können bis zu 100000 Iterationen erforderlich sein! (Z.B mit der Konfiguration [10,7,3,1])
Was passiert wenn man randomisiert die Dateninstanzen 21721 aufteilt? Wiederhole die Versuche. Erweitere den Test auf Trainingsdaten, Testdaten, und alle Dateninstanzen.
Welche Dateninstanzen (Experimente) fallen besonders auf (also bezüglich der Zielvariable)? Was macht die Lösung dieses Problems so schwierig?
Verändere die Parameter der Merkmalsselektion ([x0,x1] und xdelta). Achtung: Xdelta muss bei Veränderung |a-b| angepasst werden so dass immer 10 Datenpunkte extrahiert werden (prüfe z.B. mittels des Shell this.dataXY[0])
Jetzt das Modell B (SVM) erzeugen und trainieren. Gibt es mit einer SVM brauchbare Ergebnisse? Wenn nicht, woran könnte es liegen? Beachte die Problemklassen für die SVM geeignet sind.
Schließlich ModellC KNN: Verschiedene Datensätze verwenden (dataXY, dataXYR!!!, und Partitionen) und testen (s.u.)