updates Readme, initializes folder

Vorschlag für Vorgehen
This commit is contained in:
2023-11-27 14:47:09 +01:00
parent 660536f019
commit 4d31a54547
3 changed files with 650 additions and 1 deletions

608
BlueJ-Origin/LABOR.java Normal file
View File

@@ -0,0 +1,608 @@
import java.lang.reflect.*;
import java.util.*;
import javax.swing.*;
import java.io.*;
import javax.swing.event.*;
import java.awt.*;
import java.text.*;
public class LABOR
{
Object Perzeptron;
Method methodeLernen;
Method methodeBerechne;
ArrayList<Datapoint> Trainingset = new ArrayList<Datapoint>();
ArrayList<FormVisualization> listener = new ArrayList<FormVisualization>();
/* Das Labor-Objekt benötigt bei der Objekterzeugung ein Perzeptron, das
* vorher schon erzeugt werden musste. Man kann es in BlueJ übergeben,
* indem man einfach auf das Objekt klickt.
*/
public LABOR(Object perzeptron) throws Exception {
Perzeptron = perzeptron;
Class<?> pClass = Perzeptron.getClass();
Method[] methods = pClass.getDeclaredMethods();
ArrayList<Method> mlist = new ArrayList<Method>();
// Auswertemethode finden
for(int i=0;i<methods.length;++i){
if((methods[i].getReturnType().getName()).equals("double")
&& !Modifier.isPrivate( methods[i].getModifiers())
&& !Modifier.isProtected( methods[i].getModifiers())
&& methods[i].getParameterCount()==2
&& methods[i].getParameterTypes()[0].getName().equals("double")
&& methods[i].getParameterTypes()[1].getName().equals("double")
){
mlist.add(methods[i]);
}
}
if(mlist.size()==0){
JOptionPane.showMessageDialog(null,"Es wurde keine passende Methode zum Auswerten eines Datenpunktes gefunden.\n" +
"Es wird erwartet, dass es eine Methode gibt, die als Parameter zwei double-Werte bekommt und\n"+
"einen double-Wert zurückliefert.\n\n"+
"Beispiel:\n"+
"public double berechneAusgabe(double x, double y)");
throw new Exception("Keine Auswertungsmethode für das Perzeptron gefunden");
}
else if(mlist.size()==1){
methodeBerechne=mlist.get(0);
}
else{
FormMethodSelector formMS = new FormMethodSelector( mlist.toArray(new Method[0] ), FormMethodSelector.BERECHNE );
formMS.setVisible(true);
if(formMS.selectedMethodName==null){
throw new Exception("Keine Auswertungsmethode für das Perzeptron gewählt");
}
else{
for(int i=0;i<mlist.size();++i){
if(mlist.get(i).getName().equals(formMS.selectedMethodName)){
methodeBerechne=mlist.get(i);
}
}
}
}
mlist.clear();
// Trainingsmethode finden
for(int i=0;i<methods.length;++i){
if((methods[i].getReturnType().getName()).equals("void")
&& !Modifier.isPrivate( methods[i].getModifiers())
&& !Modifier.isProtected( methods[i].getModifiers())
&& methods[i].getParameterCount()==3
&& methods[i].getParameterTypes()[0].getName().equals("double")
&& methods[i].getParameterTypes()[1].getName().equals("double")
&& methods[i].getParameterTypes()[1].getName().equals("double")
){
mlist.add(methods[i]);
}
}
if(mlist.size()==0){
JOptionPane.showMessageDialog(null,"Es wurde keine passende Methode zum Lernen eines Traingsdatenpunktes gefunden.\n" +
"Es wird erwartet, dass es eine Methode gibt, die als Parameter drei double-Werte bekommt und\n"+
"keinen Wert zurückliefert.\n\n"+
"Beispiel:\n"+
"public void lerne(double x, double y, double label)");
throw new Exception("Keine Trainingssmethode für das Perzeptron gefunden");
}
else if(mlist.size()==1){
methodeLernen=mlist.get(0);
}
else{
FormMethodSelector formMS = new FormMethodSelector( mlist.toArray(new Method[0] ), FormMethodSelector.TRAINIERE );
formMS.setVisible(true);
if(formMS.selectedMethodName==null){
throw new Exception("Keine Methode zum Training für das Perzeptron gewählt");
}
else{
for(int i=0;i<mlist.size();++i){
if(mlist.get(i).getName().equals(formMS.selectedMethodName)){
methodeLernen=mlist.get(i);
}
}
}
}
}
/* Hier kann man eine Datei öffnen, die Trainingsdaten enthält. Sie endet mit .csv und muss
* sich im selben Verzeichnis, wie das BlueJ-Projekt befinden. Oftmals heißt so eine Datei
* "daten.csv"
*/
public void ladeTrainingsdaten(String Dateiname){
Trainingset.clear();
File file= new File(Dateiname);
if(!file.exists()){
file= new File(Dateiname + ".csv");
if(!file.exists()){
JOptionPane.showMessageDialog(null,"Die Datei mit dem Bezeichner " + Dateiname + "\n" +
"konnte nicht gefunden werden. Prüfen Sie, ob sich wirklich diese Datei\n" +
"in dem Verzeichnis befindet und ob auch die Endung stimmt.\n"+
"Üblicherweise lautet die Dateiendung .csv");
return;
}
}
try (Scanner scanner = new Scanner(file)) {
scanner.nextLine();
NumberFormat format = NumberFormat.getInstance(Locale.GERMAN);
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
String[] tokens = line.split(";");
Datapoint d = new Datapoint();
d.label=tokens[1].equals("1")?1:0;
Number number = format.parse(tokens[2]);
double dv = number.doubleValue();
d.x1=dv;
number = format.parse(tokens[3]);
dv = number.doubleValue();
d.x2=dv;
Trainingset.add(d);
}
} catch (Exception ex) {
JOptionPane.showMessageDialog(null,"Die Datei scheint fehlerhaft zu sein.\n" +
"Sie konnte leider nicht gelesen werden.");
}
UpdateListener();
JOptionPane.showMessageDialog(null,"Es wurden Trainingsdaten mit " + Trainingset.size() + " Datenpunkten eingelesen");
}
/* Die Delta-Lernregel wird einmal für
* alle Trainingsdatenpunkte durchgeführt.
*/
public void trainiereEinmal(){
ArrayList<Datapoint> set = (ArrayList<Datapoint>)Trainingset.clone();
Collections.shuffle(set);
for (Datapoint dp : set) {
Object[] par = new Object[3];
par[0]=dp.x1;
par[1]=dp.x2;
par[2]=dp.label;
try{
methodeLernen.invoke(Perzeptron, par);
}
catch(Exception exc){
}
}
UpdateListener();
}
private double perzBerechne(double x1, double x2){
Object[] par = new Object[2];
par[0]=x1;
par[1]=x2;
double ret = -1;
try{
ret= (double)methodeBerechne.invoke(Perzeptron,par);
}
catch(Exception e){}
return ret;
}
private int testeAnhandTrainingsdaten(){
int fehler=0;
for(Datapoint dp: Trainingset){
Object[] par = new Object[2];
par[0]=dp.x1;
par[1]=dp.x2;
try{
double l = (double)methodeBerechne.invoke(Perzeptron,par);
if(l!=dp.label)
fehler++;
}catch(Exception exc){
}
}
return fehler;
}
/*
* Die Methode testet, ob alle Trainingsdaten richtig klassifiziert werden.
*/
public void überprüfePerzeptron(){
int fehler = testeAnhandTrainingsdaten();
if(fehler==0){
JOptionPane.showMessageDialog(null,"Alle Trainingsdaten wurden richtig klassifiziert.\n" +
"Ein weiteres Training ist nicht erforderlich");
}
else{
JOptionPane.showMessageDialog(null,"Es wurden " + fehler + " Datenpunkte der Trainingsdaten\n" +
"falsch klassifiziert.");
}
}
/*
* Die Delta-Lernregel wird auf alle Trainingsdaten angewendet,
* bis entweder alle Trainingsdaten richtig klassifiziert werden,
* oder die Anzahl der Wiederholungen den Wert des Übergabeparameters
* überschreiten.
*/
public void trainiere(int maximaleAnzahlAnDurchläufen){
for(int i=0;i<maximaleAnzahlAnDurchläufen;++i){
trainiereEinmal();
if(testeAnhandTrainingsdaten()==0){
break;
}
}
}
/*
* Ein Fenster öffnet sich, das die Trainingsdaten anzeigt und auch,
* wir das Perzeptron die lineare Separierung durchführt.
*/
public void visualisiere(){
FormVisualization f = new FormVisualization(this);
listener.add(f);
}
private void UpdateListener(){
for(FormVisualization f: listener){
f.canvas.repaint();
}
}
class MyCanvas extends JComponent
{
FormVisualization viz;
public MyCanvas(FormVisualization f){
super();
viz=f;
this.setBackground(Color.WHITE);
}
public void paintComponent(Graphics g){
super.paintComponents(g);
Graphics2D g2d = (Graphics2D) g;
g2d.clearRect(0, 0, 500, 500);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
double minX1=-0.1;
double minX2=-0.1;
double maxX1=3;
double maxX2=3;
for(Datapoint d : viz.training){
if(d.x1>maxX1) maxX1=d.x1;
if(d.x2>maxX2) maxX2=d.x2;
if(d.x1<minX1) minX1=d.x1;
if(d.x2<minX2) minX2=d.x2;
}
Color green = new Color(144,238,144);
Color red = new Color(240,128,128);
for(double y=0;y<500;y++){
for(double x=0;x<500;x++){
Datapoint p = new Datapoint();
p.x1=minX1 + (x)/500.0*(maxX1-minX1);
p.x2=minX2 + (500.0-y)/500.0*(maxX2-minX2);
double label=viz.labor.perzBerechne(p.x1, p.x2);
if(label==1){
g2d.setColor(green);
}
else{
g2d.setColor(red);
}
g2d.fillRect((int)x, (int)y, 1, 1);
}
}
// Achsen
g2d.setColor(Color.BLACK);
g2d.setStroke(new BasicStroke(3));
double vaxs = ((0-minX1)/(maxX1-minX1))*500.0;
double vays = 500;
double vaxe = ((0-minX1)/(maxX1-minX1))*500.0;
double vaye = 0;
ZeichnePfeil(g2d,(int)vaxs, (int)vays, (int)vaxe, (int)vaye,10,10);
double haxs = 0;
double hays = 500.0-((0-minX2)/(maxX2-minX2))*500.0;
double haxe = 500;
double haye = 500.0-((0-minX2)/(maxX2-minX2))*500.0;
ZeichnePfeil(g2d,(int)haxs, (int)hays, (int)haxe, (int)haye,10,10);
g2d.setFont(new Font("TimesRoman", Font.PLAIN, 16));
g2d.drawString("x", 477, (int)(haye-4));
g2d.setFont(new Font("TimesRoman", Font.PLAIN, 10));
g2d.drawString("1", 483, (int)(haye-2));
g2d.setFont(new Font("TimesRoman", Font.PLAIN, 16));
g2d.drawString("x", (int)(vaxe+4), 21);
g2d.setFont(new Font("TimesRoman", Font.PLAIN, 10));
g2d.drawString("2", (int)(vaxe+11), 23);
// Trainingspunkte
g2d.setStroke(new BasicStroke(1));
for(Datapoint d : viz.training){
if(d.label==0){
g2d.setColor(Color.RED);
}
else{
g2d.setColor(Color.GREEN);
}
double px = ((d.x1-minX1)/(maxX1-minX1))*500.0;
double py = 500.0-((d.x2-minX2)/(maxX2-minX2))*500.0;
g.fillOval((int)px-4, (int)py-4, 9, 9);
g2d.setColor(Color.BLACK);
g.drawOval((int)px-4, (int)py-4, 9, 9);
}
}
private void ZeichnePfeil(Graphics2D g, int x1, int y1, int x2, int y2, int d, int h) {
int dx = x2 - x1, dy = y2 - y1;
double D = Math.sqrt(dx*dx + dy*dy);
double xm = D - d, xn = xm, ym = h, yn = -h, x;
double sin = dy / D, cos = dx / D;
x = xm*cos - ym*sin + x1;
ym = xm*sin + ym*cos + y1;
xm = x;
x = xn*cos - yn*sin + x1;
yn = xn*sin + yn*cos + y1;
xn = x;
int[] xpoints = {x2, (int) xm, (int) xn};
int[] ypoints = {y2, (int) ym, (int) yn};
g.drawLine(x1, y1, x2, y2);
g.fillPolygon(xpoints, ypoints, 3);
}
public Datapoint isClicked(double x, double y){
Datapoint ret = null;
double minX1=-0.1;
double minX2=-0.1;
double maxX1=3;
double maxX2=3;
for(Datapoint d : viz.training){
if(d.x1>maxX1) maxX1=d.x1;
if(d.x2>maxX2) maxX2=d.x2;
if(d.x1<minX1) minX1=d.x1;
if(d.x2<minX2) minX2=d.x2;
}
for(Datapoint d : viz.training){
double px = ((d.x1-minX1)/(maxX1-minX1))*500.0;
double py = 500.0-((d.x2-minX2)/(maxX2-minX2))*500.0;
if(Math.sqrt((px-x)*(px-x)+(py-y)*(py-y))<=5.0)
return d;
}
return ret;
}
}
class Datapoint {
public double x1;
public double x2;
public double label;
}
public class FormMethodSelector extends javax.swing.JDialog implements ListSelectionListener {
Method[] methods;
public String selectedMethodName;
int type;
public static final int BERECHNE=1;
public static final int TRAINIERE=2;
public FormMethodSelector(Method[] methods, int type) {
setModal(true);
initComponents();
this.methods=methods;
this.type=type;
DefaultListModel listModel = new DefaultListModel();
for(int i=0;i<methods.length;++i){
listModel.addElement(methods[i].getName());
}
jListMethods.setModel(listModel);
jListMethods.addListSelectionListener(this);
}
private void initComponents() {
jLabel1 = new javax.swing.JLabel();
jScrollPane1 = new javax.swing.JScrollPane();
jListMethods = new javax.swing.JList<>();
jButtonOk = new javax.swing.JButton();
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
if(type==BERECHNE)
jLabel1.setText("Wählen Sie die Methode aus, die bewirkt, dass das Perzeptron einen Datenpunkt auswertet:");
else
jLabel1.setText("Wählen Sie die Methode aus, die bewirkt, dass das Perzeptron einen Schritt der Delta-Lernregel durchführt:");
jListMethods.setModel(new javax.swing.AbstractListModel<String>() {
String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" };
public int getSize() { return strings.length; }
public String getElementAt(int i) { return strings[i]; }
});
jScrollPane1.setViewportView(jListMethods);
jButtonOk.setText("Weiter");
jButtonOk.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jButtonOkActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
.addComponent(jScrollPane1)
.addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addComponent(jButtonOk))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jLabel1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 98, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jButtonOk)
.addContainerGap(10, Short.MAX_VALUE))
);
pack();
}// </editor-fold>
private void jButtonOkActionPerformed(java.awt.event.ActionEvent evt) {
this.setVisible(false);
}
public void valueChanged(ListSelectionEvent e) {
selectedMethodName = jListMethods.getSelectedValue();
}
// Variables declaration - do not modify
private javax.swing.JButton jButtonOk;
private javax.swing.JLabel jLabel1;
private javax.swing.JList<String> jListMethods;
private javax.swing.JScrollPane jScrollPane1;
// End of variables declaration
}
public class FormVisualization extends javax.swing.JFrame {
ArrayList<Datapoint> training;
LABOR labor;
public FormVisualization(){
initComponents();
training = new ArrayList<Datapoint>();
setVisible(true);
}
public FormVisualization(LABOR l) {
initComponents();
training = l.Trainingset;
labor=l;
setVisible(true);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
canvas = new MyCanvas(this);
jLabel1 = new javax.swing.JLabel();
jLabel2 = new javax.swing.JLabel();
jLabel3 = new javax.swing.JLabel();
jButtonClearDatapoints = new javax.swing.JButton();
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
getContentPane().setLayout(null);
canvas.addMouseListener(new java.awt.event.MouseAdapter() {
public void mousePressed(java.awt.event.MouseEvent evt) {
canvasMouseClicked(evt);
}
});
getContentPane().add(canvas);
canvas.setBounds(10,10,500, 500);
jLabel1.setText("Rechter Mausklick: Datenpunkt mit Label 0 wird hinzugefügt");
getContentPane().add(jLabel1);
jLabel1.setLocation(20, 540);
jLabel1.setSize(500, 20);
jLabel2.setText("Linker Mausklick: Datenpunkt mit Label 1 wird hinzugefügt");
getContentPane().add(jLabel2);
jLabel2.setLocation(20,520);
jLabel2.setSize(500, 20);
jLabel3.setText("Klick auf Datenpunkt: Datenpunkt wird entfernt");
getContentPane().add(jLabel3);
jLabel3.setLocation(20,560);
jLabel3.setSize(500, 20);
jButtonClearDatapoints.setActionCommand("<html><center>Alle Datenpunkte<br>löschen</center></html>");
jButtonClearDatapoints.setLabel("<html><div align=\"center\">Alle Datenpunkte<br>löschen</div></html>");
jButtonClearDatapoints.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
jButtonClearDatapointsMouseClicked(evt);
}
});
getContentPane().add(jButtonClearDatapoints);
jButtonClearDatapoints.setBounds(370, 520, 140, 60);
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent e) {
CloseHandler();
}
});
pack();
this.setSize(540, 630);
}// </editor-fold>
private void CloseHandler(){
labor.listener.remove(this);
}
private void canvasMouseClicked(java.awt.event.MouseEvent evt) {
Datapoint clickedPoint=canvas.isClicked(evt.getPoint().getX(), evt.getPoint().getY());
if(clickedPoint!=null){
training.remove(clickedPoint);
}
else{
double l=0;
if(SwingUtilities.isLeftMouseButton(evt)) l=1;
Datapoint p = new Datapoint();
double x = evt.getPoint().getX();
double y = 500.0-evt.getPoint().getY();
double minX1=-0.1;
double minX2=-0.1;
double maxX1=3;
double maxX2=3;
for(Datapoint d : training){
if(d.x1>maxX1) maxX1=d.x1;
if(d.x2>maxX2) maxX2=d.x2;
if(d.x1<minX1) minX1=d.x1;
if(d.x2<minX2) minX2=d.x2;
}
p.label=l;
p.x1=minX1 + x/500.0*(maxX1-minX1);
p.x2=minX2 + y/500.0*(maxX2-minX2);
training.add(p);
}
canvas.repaint();
labor.UpdateListener();
}
private void jButtonClearDatapointsMouseClicked(java.awt.event.MouseEvent evt) {
training.clear();
labor.UpdateListener();
canvas.repaint();
}
MyCanvas canvas;
private javax.swing.JButton jButtonClearDatapoints;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel2;
private javax.swing.JLabel jLabel3;
}
}

25
BlueJ-Origin/daten.csv Normal file
View File

@@ -0,0 +1,25 @@
Beschriftung;Klasse;Kalorien;Eiweiß
Erbsen;1;37;3
Gurken;1;4;0
Karotten;1;29;1
Spinat;1;12;2
Tomaten;1;16;1
Apfel;1;49;0
Aprikose;1;49;1
Banane;1;70;1
Wassermelone;1;12;0
Aal;0;204;9
Forelle;0;50;10
Thunfisch;0;239;22
Lachs;0;137;13
Makrele;0;124;12
Ente;0;192;15
Pute;0;122;23
Schwein;0;167;18
Croissant;0;280;0
Donut;0;295;3
Semmel;0;276;7
Toastbrot;0;326;10
Weizenbrot;0;235;8
Rührkuchen;0;430;6
Roggenbrot;0;243;6
1 Beschriftung Klasse Kalorien Eiweiß
2 Erbsen 1 37 3
3 Gurken 1 4 0
4 Karotten 1 29 1
5 Spinat 1 12 2
6 Tomaten 1 16 1
7 Apfel 1 49 0
8 Aprikose 1 49 1
9 Banane 1 70 1
10 Wassermelone 1 12 0
11 Aal 0 204 9
12 Forelle 0 50 10
13 Thunfisch 0 239 22
14 Lachs 0 137 13
15 Makrele 0 124 12
16 Ente 0 192 15
17 Pute 0 122 23
18 Schwein 0 167 18
19 Croissant 0 280 0
20 Donut 0 295 3
21 Semmel 0 276 7
22 Toastbrot 0 326 10
23 Weizenbrot 0 235 8
24 Rührkuchen 0 430 6
25 Roggenbrot 0 243 6

View File

@@ -1,3 +1,19 @@
# PerzeptronLabor # PerzeptronLabor
Eine Portierung des Perzeptron Labors von Christoph Gräßl für die Online-IDE, entwickelt unter MIT-Lizenz. Eine Portierung des Perzeptron Labors von Christoph Gräßl für die Online-IDE, entwickelt unter MIT-Lizenz.
## Vorschlag fürs Vorgehen:
1. Einlesen der Datenpunkte in der CSV-Datei (`ladeTrainigsdaten`)
2. Einrichten eines grafischen Koordinatensystems
3. Visualisieren der eingelesenen Datenpunkte
4. Visualisieren der kategorisierten Bereiche (rote/grüne Fläche)
5. Hinzufügen neuer Datenpunkte durch Mausklick (rechts/links)
6. Methode `trainiereEinmal`, mit Button#
7. Methode `überprüfePerzeptron`
8. Überlegen: wie umsetzen: trainiere n-Mal?
Entwicklung jedes Schrittes auf eigenem Branch, mergen in main, wenn zwei von uns einverstanden sind.
Verbesserungsvorschläge sind herzlich willkommen!