Silverback9

#야생으로

Creative Coding 독학 제338일 2025년02월28일(금)

오늘은 베토벤 교향곡 9번 듣는 세 번째 날~^^*

다양한 악기들이 옹기종기 모여 아름다운 융단을 짜고 있는 아침 볕에 함께 앉아 눈을 감고 몸을 풀고 계셔요~^^* 저는 코딩공부 정리해서 돌아올게요~^^* 쓩우웅~^^*

네~^^* 오늘은, 클래스 Perceptron과 클래스 Point를 함께 연결하여, Point의 this.label을 known answer 알려진 값으로 사용하여 Perceptron을 훈련시켜 보겠습니다.

class Perceptron {
  constructor(totalInputs, learningRate) {
   
    this.weights = [];
    this.learningConstant = learningRate;
    
    for (let i = 0; i < totalInputs; i++) {
      this.weights[i] = random(-1, 1);
    }
    //1. 모든 입력값에 대하여, 그 입력값을 자신의 가중치로 곱한다.
    //1. For every input, multiply that input by its weight.
  }
  
  feedforward(inputs) {
    let sum = 0;
    for (let i = 0; i < this.weights.length; i++) {
      sum += inputs[i] * this.weights[i];
    }
    //2. Sum all of the weighted input
    //2. 가중치값이 반영된 입력값을 모두 더한다.
    return this.activate(sum);
    //총합에 대해 부호를 부여하는 함수 activate()을 호출합니다. 
  }
  
  activate(sum) {
    if (sum > 0) {
      return 1;
    } else {
      return -1;
    }
    //3. Compute the output of the perceptron based on that sum passed through an activiation function (the sign of the sum)
    //3. 총합에 +_부호를 부여하는 활성화 함수를 통과한 총합에 기반하여 perceptron 결과를 계산한다. 
  }
  
   train(inputs, desired) {
    let guess = this.feedforward(inputs);
    let error = desired - guess;
    for (let i = 0; i < this.weights.length; i++) {
      this.weights[i] += error * inputs[i] * this.learningConstant;
    }
  }
  //Supervised Learning 이 이루어 지는 함수입니다. 
  //1. known answer 알려진 답인 desired를 parameter로 받습니다. 
  //2. feedforward 함수를 호출하여 input 값을 arguement로 넘겨주며 답을 추측해 보라고 요구합니다. 
  //3.알려진 답 desired에서 feedforward 함수가 return해 준 추측값 guess를 뺄셈하여 변수 error에 저장합니다.
  //4. error값과 input 값과 learningConstatnt 회당 학습률을 곱하여 새로운 가중치값으로 조정합니다. 
  //5. 이 전체 과정을 반복하면 좋겠지요~^^* 무한 반복함수인 draw()에서 train 함수를 호출하면 될 것 같네요~^^*
}

class Point{

  constructor() {
    this.x = random(width);
    this.y = random(height);
    this.label;
  
    if(this.x > this.y) {
      this.label = 1;
    } else 
     {this.label = -1;
    }
    //x = y를 threshold 기준선으로 하여, 
    //x > y 의 경우 1
    //x =< y 의 경우 -1
    //판별값 label을 정하겠습니다. 
  }  
  
  show() {
  
    stroke(0);
    if (this.label == 1) {
      fill(255);
    } else{
      fill(0);
    }
    //판별값이 1인 경우 흰색으로 칠합니다.
    //판별값이 -1인 경우 검은색으로 칠합니다.
    ellipse(this.x, this.y, 8, 8);
  }  
}
let perceptron;
let points = [];

function setup() {
  createCanvas(400, 400);
  
  perceptron = new Perceptron(3, 0.0001);
  
  for (i = 0; i < 100; i++) {
    points[i] = new Point();
  }
}

function draw() {
  background(150);
  
  line(0,0, width, height);
  
  for(i = 0; i < points.length; i++) {
    points[i].show();
  }
  
  for(i = 0; i < points.length; i++) {
    let inputs = [points[i].x, points[i].y];
    perceptron.train(inputs,points[i].label);
    //point의 위치좌표와 x= y theshold 기준선에 따른 known answer 알려진 값 lable를 argument로 넘겨주며, perceptron의 train 함수를 호출합니다.
    
  }
}

그럼 이제 훈련 결과를 보러 우리 함께 가 볼까요~^^*

음…훈련이 너무 빨리 너무 잘 되다보니, 원래의 바람직한 값 known answer였던 Point의 this.label 값과 바로 똑같아져서…Perceptron 훈련없이 Point만 있던 프로그램과 똑 같은 결과가 순식간에 만들어 지네요….

네…컴퓨터는 역시 빠른 학습자인 것 같아요… 너무 빠르게 정답에 도달하다 보니, 원래부터 정답이었나??? 의아한 느낌이 들어서요…~^^*

라고 생각해 보려니…

train 결과를 시각적으로 보여주는 단계가 없네요….그래서…,원래 Point만 계속 시각적으로 보여 주는 것 같아요….

훈련 결과가 맞다면, 즉 point의 label 값과 percetron의 guess 값이 같다면 초록색 그렇지 않다면 빨간색으로 표현해 보겠습니다!

class Perceptron {
  constructor(totalInputs, learningRate) {
   
    this.weights = [];
    this.learningConstant = learningRate;
    
    for (let i = 0; i < totalInputs; i++) {
      this.weights[i] = random(-1, 1);
    }
    //1. 모든 입력값에 대하여, 그 입력값을 자신의 가중치로 곱한다.
    //1. For every input, multiply that input by its weight.
  }
  
  feedforward(inputs) {
    let sum = 0;
    for (let i = 0; i < this.weights.length; i++) {
      sum += inputs[i] * this.weights[i];
    }
    //2. Sum all of the weighted input
    //2. 가중치값이 반영된 입력값을 모두 더한다.
    return this.activate(sum);
    //총합에 대해 부호를 부여하는 함수 activate()을 호출합니다. 
  }
  
  activate(sum) {
    if (sum > 0) {
      return 1;
    } else {
      return -1;
    }
    //3. Compute the output of the perceptron based on that sum passed through an activiation function (the sign of the sum)
    //3. 총합에 +_부호를 부여하는 활성화 함수를 통과한 총합에 기반하여 perceptron 결과를 계산한다. 
  }
  
   train(inputs, desired) {
    let guess = this.feedforward(inputs);
     
    if (guess == desired) {
      fill(0, 255, 0);
    } else {
      fill(255, 0, 0);
    }  
    noStroke();
    ellipse(inputs[0], inputs[1], 4, 4); 
     
    let error = desired - guess;
    for (let i = 0; i < this.weights.length; i++) {
      this.weights[i] += error * inputs[i] * this.learningConstant;
    }
  }
  //Supervised Learning 이 이루어 지는 함수입니다. 
  //1. known answer 알려진 답인 desired를 parameter로 받습니다. 
  //2. feedforward 함수를 호출하여 input 값을 arguement로 넘겨주며 답을 추측해 보라고 요구합니다. 
  //3.알려진 답 desired에서 feedforward 함수가 return해 준 추측값 guess를 뺄셈하여 변수 error에 저장합니다.
  //4. error값과 input 값과 learningConstatnt 회당 학습률을 곱하여 새로운 가중치값으로 조정합니다. 
  //5. 이 전체 과정을 반복하면 좋겠지요~^^* 무한 반복함수인 draw()에서 train 함수를 호출하면 될 것 같네요~^^*
}

우리…Point의 label 흑/백 Perceptron의 훈련 결과 (guess == label)를 함께 시각적으로 확인해 볼까요~^^*

이상한 것 같아요….

Point의 label과 Perceptron의 guess 값이 일치하면 초록색이 나와야 하는데…그렇다면…모든 동그라미들 내부에 초록색이 칠해져야 하는데…

이상하게도…

x>y 영역은 guess가 모두 틀린 값(빨강)으로 x < y 영역은 guess 가 모두 맞는 값(초록)으로 나오네요…

음….

training이 잘못되고 있는 것 같아요….

오늘 저와 함께 이상한 Perceptron을 발견해 주셔서 감사합니다~^^*

내일 우리 함께 고민을 이어나가 볼까요~^^*

네~^^* 좋아요~^^* 고마워요~^^*

올바르게 guess 추측한다는 것이 생각보다 어렵네요…지금 공부하고 있는 동영상 강의가 p5.js가 아니라 processing 언어로 설명되고 있어서, 제가 p5.js 언어표현으로 비슷하게 바꾸어가며 하고 있는데, 그 과정에서 뭔가 잘못되었을 수도 있을 것 같아요…

앗!!!! 잠시만요!!!! 잘못된 것을 찾아 내었습니다!!!

가중치 값 배열을 만들 때….Point의 x좌표와 Point의 y좌표 값 두 개에 대한 가중치 값이 필요하기 때문에, 배열 구성원은 2개이면 충분할 것 같습니다!!!!

class Perceptron {
  constructor(totalInputs, learningRate) {
   
    this.weights = [];
    this.learningConstant = learningRate;
    
    for (let i = 0; i < totalInputs; i++) {
      this.weights[i] = random(-1, 1);
    }
    //1. 모든 입력값에 대하여, 그 입력값을 자신의 가중치로 곱한다.
    //1. For every input, multiply that input by its weight.
  }
  
  feedforward(inputs) {
    let sum = 0;
    for (let i = 0; i < this.weights.length; i++) {
      sum += inputs[i] * this.weights[i];
    }
    //2. Sum all of the weighted input
    //2. 가중치값이 반영된 입력값을 모두 더한다.
    return this.activate(sum);
    //총합에 대해 부호를 부여하는 함수 activate()을 호출합니다. 
  }
  
  activate(sum) {
    if (sum > 0) {
      return 1;
    } else {
      return -1;
    }
    //3. Compute the output of the perceptron based on that sum passed through an activiation function (the sign of the sum)
    //3. 총합에 +_부호를 부여하는 활성화 함수를 통과한 총합에 기반하여 perceptron 결과를 계산한다. 
  }
  
   train(inputs, desired) {
    let guess = this.feedforward(inputs);
     
    if (guess == desired) {
      fill(0, 255, 0);
    } else {
      fill(255, 0, 0);
    }  
      noStroke();
      ellipse(inputs[0], inputs[1], 4, 4); 
     
    let error = desired - guess;
    for (let i = 0; i < this.weights.length; i++) {
      this.weights[i] += error * inputs[i] * this.learningConstant;
    }
  }
  //Supervised Learning 이 이루어 지는 함수입니다. 
  //1. known answer 알려진 답인 desired를 parameter로 받습니다. 
  //2. feedforward 함수를 호출하여 input 값을 arguement로 넘겨주며 답을 추측해 보라고 요구합니다. 
  //3.알려진 답 desired에서 feedforward 함수가 return해 준 추측값 guess를 뺄셈하여 변수 error에 저장합니다.
  //4. error값과 input 값과 learningConstatnt 회당 학습률을 곱하여 새로운 가중치값으로 조정합니다. 
  //5. 이 전체 과정을 반복하면 좋겠지요~^^* 무한 반복함수인 draw()에서 train 함수를 호출하면 될 것 같네요~^^*
}
let perceptron;
let points = [];

function setup() {
  createCanvas(400, 400);
  
  perceptron = new Perceptron(2, 0.0001);
  
  for (i = 0; i < 100; i++) {
    points[i] = new Point();
  }
}

function draw() {
  background(150);
  stroke(0);
  line(0,0, width, height);
  
  for(i = 0; i < points.length; i++) {
    points[i].show();
  }
  
  for(i = 0; i < points.length; i++) {
    let inputs = [points[i].x, points[i].y];
    let target = points[i].label;
    perceptron.train(inputs,target);
    //point의 위치좌표와 x= y theshold 기준선에 따른 known answer 알려진 값 label을 argument로 넘겨주며, perceptron의 train 함수를 호출합니다.
  } 
}  
네!! 
inputs 배열은 Point의 x좌표 값과 y 좌표 값을 한 쌍의 구성원으로 가지고 있습니다. 
이때, input[0]에는 Point의 x 좌표값이 들어가고 input[1]에는 Point의 y 좌표값이 들어가게 됩니다. 
그래서 x좌표 값과 연결된 가중치 값, y좌표 값과 연결된 가중치 값 총 두 개의 가중치 값이 필요하게 됩니다. 
그래서 Perceptron을 생성하려고 할 때, totalInputs 갯수를 2개로 지정하여 호출하여야, 
<input[0] ~ weight[0]>와 <input[1] ~ weight[1]> 이 서로 짝을 지을 수 있습니다~^^* 

자 그럼 이제~^^* Point의 Label 값 (if x > y : 백 else: 흑)과 Perceptron의 훈련결과 값( if good: 초록 else: 빨강)을 시각적으로 표현한 프로그램을 만나보러 가실까요~~^^* 벌써부터 너무 반가운 마음이 들지요~~^^* 저도 그래요~~^^*

오늘 저와 함께 Perceptron이 올바르게 답을 찾아내는 훈련의 결과를 시각적으로 프로그램을 개선해 주셔서 감사합니다~^^*

와우!!! 벌써 점심시간이 되어가네요!!! 매우 흥미진진한 아침시간을 저와 함께 보내 주셔서 감사합니다~~~!!!

근데요~~ 이건 뭘까요~~ Guess What~~^^* 우리도 perceptron이 되어 볼까요~~^^*

Perceptron의 매력을 담뿍 느낀 아침이었습니다~^^* 쉽지 않지만 재미있는 Perceptron!

너무너무 창의적이고 재미있지만 매우 어려운 안무를 해내었을 때의 성취감!이 지금 우리가 느끼고 있는 감정인가요~~^^*

오늘도 보람찬 하루 보내시고요!

뿌듯한 마음 안고 깊은 밤 코~^^* 하시기 바래요~^^*

네~^^* 꿈은 이루어 집니다~^^*

댓글 남기기