Android 에뮬레이터 실행시 

Emulator: Process finished with exit code -1073741819 (0xC0000005)

라는 에러로 실행이 되지 않는 경우 

avd config 파일에서 hw.gpu.mode = off

출처 : https://stackoverflow.com/a/51740954

지치지 않고 제대로 공부하는 7가지 방법 요약

동영상 링크

https://www.youtube.com/watch?v=P2rpSiACoVQ&t=890s


어떻게 해야 우울해 지지 않고 하루에 10시간 이상 공부할 수 있을까? 어떻게 해야 그것에 익숙해질 수 있을까?

이에 대해 쿼라 에서 유명 교수님의 답변을 요약 한 내용입니다.

  1. 나는 당신은 그것을 할수 없다고 생각한다.

  2. 나는 당신이 그것을 시도해서도 안된다고 생각한다.

  3. 아무도 그렇게 많이 공부하지 않는다.

사람이 하루에 3시간이상 열심히 집중하는 건 매우 드물고 바람직하지 않는다

하지만 그래도 꼭 하고 싶다면 다음 7가지 내용을 기억하자.

꾸준하고 제대로 된 공부법은 무엇인가?

1. 하루에 7.5 이상 공부하지 마세요.

당신은 매우 지칠 것이다. 더한다고 해도 도움이 되지 않는다.

당신이 지쳤을 때 당신은 배울수 없다.

2. 일주일에 하루를 쉬세요.

쉬는 날 당신이 좋아하는 것을 쉬세요. 당신의 일은 배움을 위한 것이지 일찍 죽을려고 하는 것이 아니다.

휴식하는 동안에도 머리는 공부하고 있다. 힘들거나 잘 안풀리면 그냥 휴식을 하자.

3. 공부 계획를 세우세요.

각 과목에서 가장 중요한 지식은 무엇인지를 파악하고 그것에 대해 가장 쉽고 효율적으로 공부할 수 있는 공부방법에 대한 계획을 세우도록 하자.

목표를 세우는 이유

  • 무엇을 해야 하는지 알려준다.

  • 동기부여 수단

  • 현재 모습을 구체적으로 보게한다.

4. 낮은 난이도에서 높은 난이도 순으로 공부하세요.

큰틀에서 핵심 아이디어에 익숙해지고 그런 다음 세부 사항에 집중하자.

공부한 다음 피드백으로 그 내용을 다시 요약해보는 습관을 가져보자.

처음부터 디테일 집중하지 말고 크게 보고 작게 넘어가자.

그리고 작은 성공을 많이 이루어 내서 성공의 경험을 많이 맛보자.

기대 수준을 낮춰 작은 성공에 도전하자.

반꼴찌 40등 -> 5등은 어렵다.

하지만 40등 -> 30등은 쉬우므로 성취감도 느끼고 큰틀에서 먼저 공부하는 방식

5. 낮잠을 많이 자자

당신의 기분을 새롭게 하고 당신이 공부 한 것을 기억할 확률을 증가시킨다.

잠자는 것은 메모를 통합시키는 데 도움이 된다.

6. 2.5시간 동안 한 주제를 공부하세요.

그런 다음 다른것으로 전환하자. 이어서 계속 반복하자.

2.5시간동안 한 주제를 반복 공부하자.

7. 읽은 내용 내용을 요약하자.

어렵게 공부하면 잊어지기가 어렵다.

책을 읽고 책을 내려놓고 읽은 것을 요약하자.

요약할 때는 읽은 내용을 보지말고 하자.

동일한 자료를 반복적으로 보지 않고 기억해내는 연습을 해야한다.

자료를 복습하는 건 아무 소용이 없다.

작업의 실제적인 어려움 없이 일하는 것이다. (책을 자꾸 들춰보면서 보는 방법 비추천)

책을 읽을때도 강조를 표시하거나 밑줄을 긋지 말도록 하자. (집중력 저하)

요약 = 요약, 암송, 토론, 발표, 글쓰기등등

공부한 내용을 어떻게든 밖으로 표출하는 것을 추천한다.

힘들지만 계속 하다보면 쉬워진다 = 뇌의 가소성

정리

1,2는 최대 7.5시간 공부하고 휴식하자.

3,4는 계획을 세우고 늦은 난이도에서 높은 난이도로 공부하도록 하자.

5는 낮잠으로 재충전하자.

6는 한 주제 최대 7.5시간

7은 요약하기 (어렵게 공부하면 잊기가 어렵다)

책상에만 앉아 꼼짝않고 책만 보는 건 공부 하수가 하는 일

추천 공부 방법

7.5시간 공부 + 쉬는 시간 확보 +낮잠 + (커피 + 운동)= 공부 효율성 극대화

추천 책 리스트

  1. 완벽한 공부법

  2. 혼자하는 공부법




Tensorflow JS 스터디 4주차

부산에서 매주 진행되는 스터디입니다.

부산에서 다른 스터디 내용을 보실려면 카페 에서 보실수 있습니다.

https://www.udemy.com/machine-learning-with-javascript 을 같이 보면서 공부중입니다.

이전 알고리즘과 비교

  • 어떤 버킷에 공을 놓을때 어떤 위치를 갈지 예측하기 -> Classification

    • Classification 은 예를 들어 해당 메일이 스팸인지 일반 메일인지 판별하는 문제 해결시 사용된다.
  • 주변 시세에 따른 주택 가격 예측 -> Regression (선형 회귀)

주택 가격 예측 (by knn)

1544689793285

KNN 알고리즘

  • 이번 스터디는 텐서플로를 이용해서 KNN 알고리즘 하나하나 구현해본다.

1544690154341

  • 특정 기능과 예측포인트 사이를 찾고
  • 작은 것에서 큰걸로 정렬한다
  • 최상위 K 레코드들을 가져온다.
  • 이러한 최상위 레코드들을 가져온 것에서 평균을 내서 결과값 도출한다.

목표

  • 위도 경도가 주어지면 House Price 가격을 주어진 데이터에 근거해서 뽑아내는 게 목표

준비단계

1544690376477

이전 내용에서는 각각 줄마다 합쳐서 테이블을 만들었다면 이번에는 두개의 features 와 labels 로 나누어서 작업을 할 예정이다.

  • 가짜 데이터들을 먼저 만들도록 한다.

1544690502504

실습

먼저 두개의 별도의 텐서를 만든다. 하나는 위도와 경도를 저장하는 것이고 또 하나는 주택 가격을 저장한다.

const features = tf.tensor([
	[-121, 47],
  [-121.2, 46.5],
  [-122, 46.4],
  [-120.9, 46.7]  
]);
const labels = tf.tensor([
  [200],
  [250],
  [215],
  [240]
]);
const predictionPoint = tf.tensor([-121, 47]);

knn #1 features 와 predictionPoint 와 거리를 구한다.

1544691170114

1544691187998

거리를 구하는 방법을 텐서플로로 진행 해보자.

1544691243831

이 계산식을 하나씩 풀어서 진행해볼 예정이다. 그럼 시작해보자.

Distance Sub

1544691388465

...

features
  .sub(predictionPoint)
[[0 , 0 ], [-0.1999969, -0.5 ], [-1 , -0.5999985], [0.0999985 , -0.2999992]]

제곱

1544691577938

features
  .sub(predictionPoint)
  .pow(2)
[[0 , 0 ], [0.0399988, 0.25 ], [1 , 0.3599982], [0.0099997, 0.0899995]]

Sum

  • 각각의 행에 대한 텐서의 값을을 합산한다.
  • Sum(0 or 1)
    • 0의 경우 메인 축이 세로로 지정된다.
    • 1의 경우 세로측이 메인이다.

1544691705955

features
  .sub(predictionPoint)
  .pow(2)
  .sum(0)
[1.0499985, 0.6999977]

우리가 원하는 가로로 계산하길 원한다.

features
  .sub(predictionPoint)
  .pow(2)
  .sum(0)
[0, 0.2899988, 1.3599982, 0.0999992]

1544692088645

제곱근

1544692117081

features
  .sub(predictionPoint)
  .pow(2)
  .sum(1)
  .pow(0.5)
[0, 0.5385153, 1.1661896, 0.3162265]

정렬

KNN 알고리즘 순서 두번째로 정렬을 진행한다.

  • 텐서들은 정렬할 수 없는 점 유의

1544692330245

  • 거리와 라벨을 따로 떼서 정렬하면 안된다. 아래그림같이 문제가 될 수 있다.

1544692420312

Concat

두개의 텐서들을 합치기 위해 concat 을 이용한다.

1544692526090

1544692556011

features
  .sub(predictionPoint)
	.pow(2)
	.sum(1)
	.pow(0.5)
	.concat(labels)
error : Error in concat1D: rank of tensors[1] must be the same as the rank of the rest (1)

shape 들이 브로드 캐스팅을 하기 위한 조건이 아니어서 발생을 한다.

labels
	.shape

features
  .sub(predictionPoint)
	.pow(2)
	.sum(1)
	.pow(0.5)
	.shape
	
..... 브로드 캐스팅 조건이 맞지 않는다.
[4,1]
[4]

ExpandDims

  • 부족한 줄을 채워 주기 위해 expandDims() 를 적용한다.
features
  .sub(predictionPoint)
	.pow(2)
	.sum(1)
	.pow(0.5)
	.expandDims()

...
[[0, 0.5385153, 1.1661896, 0.3162265],]

1544692996609

한칸 더 생긴걸 볼수 있다.

features
  .sub(predictionPoint)
	.pow(2)
	.sum(1)
	.pow(0.5)
	.expandDims()
	.shape
...
[1,4]

아직도 label 쪽과 맞지 않는다.

  • Axis 를 1로 해서 바꿔준다.
...
.expandDims(1)
	
...
[4,1]

이제 다시 concat 을 해보자

features
  .sub(predictionPoint)
	.pow(2)
	.sum(1)
	.pow(0.5)
	.expandDims(1)
	.concat(labels)
	
....
[[0 ], [0.5385153], [1.1661896], [0.3162265], [200 ], [250 ], [215 ], [240 ]]

하지만 결과를 보면 [feature,label] 순으로 합쳐 있지 않다.

  • concat 시 축을 1로 바꿔서 해보자

1544693281622

features
  .sub(predictionPoint)
	.pow(2)
	.sum(1)
	.pow(0.5)
	.expandDims(1)
	.concat(labels, 1)
	
....
[[0 , 200], [0.5385153, 250], [1.1661896, 215], [0.3162265, 240]]

이제 정상적으로 합쳐진걸 볼수 있다.

하지만 아직 정렬에서 문제가 있다.

1544693511557

현재 이런 구조인데 이 상태로 정렬이 안된다는 것이다. 이걸 해결하기 위해서 unstack 이라는 함수로 각각의 텐서로 쪼개어서 정렬할 수 있다.

unstack

1544693583648

features
  .sub(predictionPoint)
	.pow(2)
	.sum(1)
	.pow(0.5)
	.expandDims(1)
	.concat(labels, 1)
	.unstack()

1544693730949

각각의 텐서들로 쪼개어진 걸 볼수 있다.

만약 특정 위치의 값을 뽑기 위해선 배열형태로 접근이 가능하다.

...
.unstack()[3]
...
[0.3162265, 240]

정렬의 문제

일반 오브젝트 형태를 정렬시 동작이 안된다. 아래는 잠깐 예를 들어본다

const distance = [
  { value: 10 },
  { value: 30 },
  { value: 20 }
];

distance.sort()
........
[{"value":10},{"value":30},{"value":20}]
  • sort 함수를 좀 더 확장해서 정렬을 할 수 있다.
const distance = [
  { value: 10 },
  { value: 30 },
  { value: 20 }
];

distance.sort((a,b) => {
	return a.value > b.value ? 1 : -1
})

.......
[{"value":10},{"value":20},{"value":30}]

그럼 이어서 unstack 한 부분에 적용을 해보자.

features
  .sub(predictionPoint)
	.pow(2)
	.sum(1)
	.pow(0.5)
	.expandDims(1)
	.concat(labels, 1)
	.unstack()
	.sort((a, b) => {
		a.get(0) > b.get(0) ? 1 : -1
	})

최상위 K 레코드 가져오기

정렬된 배열에서 최상위 오브젝트들을 가져오기 위해 slice 을 사용한다.

const k = 2;

features
  .sub(predictionPoint)
	.pow(2)
	.sum(1)
	.pow(0.5)
	.expandDims(1)
	.concat(labels, 1)
	.unstack()
	.sort((a, b) => {
		a.get(0) > b.get(0) ? 1 : -1
	})
	.slice(0,k)
...

[{"isDisposedInternal":false,"shape":[2],"dtype":"float32","size":2,"strides":[],"dataId":{},"id":7895,"rankType":"1"},
{"isDisposedInternal":false,"shape":[2],"dtype":"float32","size":2,"strides":[],"dataId":{},"id":7897,"rankType":"1"}]

평균 구하기

합계 ( reduce ) / count

const distance = [
  { value: 10 },
  { value: 30 },
  { value: 20 }
];

distance.reduce((acc,obj) => {
	return acc + obj.value
}, 0)

60 / 3(distance length)
.......
60 -> 20(결과)

위의 예제를 토대로 다시 본 소스에 적용해보자.

const k = 2;

features
  .sub(predictionPoint)
	.pow(2)
	.sum(1)
	.pow(0.5)
	.expandDims(1)
	.concat(labels, 1)
	.unstack()
	.sort((a, b) => {
		a.get(0) > b.get(0) ? 1 : -1
	})
	.slice(0,k)
	.reduce((acc, pair) => acc + pair.get(1), 0) / k;
	
...	
225

그럼 labels 기준으로 했을때 가장 근접한 수치가 225 로 뽑을수 있다.

여기까지가 텐서플로을 이용해서 KNN 알고리즘을 적용하는 방법이었다.

그럼 이제 다음 내용에는 실제 데이터를 가지고 작업을 해보자.

소스 준비

git clone https://github.com/StephenGrider/MLKits.git
git checkout 84f617c4549409c104aa994465101d1a8fd164e2

npm install

1544706669880

Index.js

이제 위에서 공부했던 내용을 코드로 옮겨보자.

기본적으로 모듈은 두개를 설치를 한다.

require('@tensorflow/tfjs-node');
// 만약 gpu 를 사용시 tfjs-node-gpu 적용
const tf = require('@tensorflow/tfjs');

load csv file

이미 준비된 데이터를 이용한다.

require('@tensorflow/tfjs-node');
const tf = require('@tensorflow/tfjs');
const loadCSV = require('./load-csv');

let { features, labels, testFeatures, testLabels } = loadCSV('kc_house_data.csv', {
    // 무작위로 섞기
    shuffle: true,
    // 테스트셋 지정 -> testfeatures, testlabels
    splitTest: 10,
    // features 지정 (위도, 경도) -> features
    dataColumns: ['lat', 'long'],
    // price 지정 -> label
    labelColumns: ['price'],    
});

console.log(testFeatures);
console.log(testLabels);
[ [ 47.561, -122.226 ],
  [ 47.6595, -122.186 ],
  [ 47.5081, -122.093 ],
  [ 47.5276, -122.161 ],
  [ 47.6695, -122.333 ],
  [ 47.6769, -122.36 ],
  [ 47.5442, -122.141 ],
  [ 47.699, -122.206 ],
  [ 47.3196, -122.399 ],
  [ 47.2843, -122.357 ] ]
[ [ 1085000 ],
  [ 466800 ],
  [ 425000 ],
  [ 565000 ],
  [ 759000 ],
  [ 512031 ],
  [ 768000 ],
  [ 1532500 ],
  [ 204950 ],
  [ 247000 ] ]

Tensorflow JS 작성

function knn(features, labels, predictionPoint, k) {
    return features
        .sub(predictionPoint)
        .pow(2)
        .sum(1)
        .pow(0.5)
        .expandDims(1)
        .concat(labels, 1)
        .unstack()
        .sort((a, b) => {
            a.get(0) > b.get(0) ? 1 : -1
        })
        .slice(0, k)
        .reduce((acc, pair) => acc + pair.get(1), 0) / k;
}

knn 함수 적용

  • 변수들을 텐서로 변환해서 사용해야 한다
features = tf.tensor(features);
labels = tf.tensor(labels);
testFeatures = tf.tensor(testFeatures);
testLabels = tf.tensor(testLabels);
  • 하나의 값만 우선 테스팅 하기 위해 testFeatures[0] 으로 가져온다.
const result = knn(features, labels, tf.tensor(testFeatures[0]), 10);
console.log('Guess', result, testLabels[0][0]);

.....
Guess 559100 1085000

하지만 559100 1085000 두개의 값이 가격 차이가 너무 나는 걸 볼 수 있다.

이걸 다시 개선하는 방법에 대해서 공부해보자.

정확도

  • 우선 에러가 작을수록 좋다. 이걸 측정해보자.

1544708102786

위의 공식을 코드로 구현하면 다음과 같다.

const err = (testLabels[0][0] - result) / testLabels[0][0];
console.log('Err', err * 100);

.......
Err 48.47004608294931

약 48프로 정도 에러가 발생하고 있다.

그럼 전체 testFeatures 에 대해 불정확률을 구해보면

testFeatures.forEach((testPoint, i) => {
    const result = knn(features, labels, tf.tensor(testPoint), 10);
    const err = (testLabels[i][0] - result) / testLabels[i][0];
    console.log('Err', err * 100);
});

....
Err 48.47004608294931
Err -19.77292202227935
Err -31.55294117647059
Err 1.0442477876106195
Err 26.337285902503293
Err -9.192607478844055
Err 27.200520833333336
Err 63.51712887438825
Err -172.79824347401805
Err -126.35627530364373

이러한 내용에 좀 더 정확도를 올리기 위해 좀 더 추가적인 features 를 추가해본다.

  • sqft_lot : 평방 피트
let { features, labels, testFeatures, testLabels } = loadCSV('kc_house_data.csv', {
    // 무작위로 섞기
    shuffle: true,
    // 테스트셋 지정 -> testfeatures, testlabels
    splitTest: 10,
    // features 지정 (위도, 경도) -> features
    dataColumns: ['lat', 'long', 'sqft_lot'],
    // price 지정 -> label
    labelColumns: ['price'],
});

표준화

처음에 배웠던 내용중에 normalize 하는 방법에 대해서 공부를 했었다.

1544708829630

표준화 하는 과정

1544708882008

이 방법을 지금 내용에 도입해보자.

  • 중간에 너무 범위가 커서 제대로된 표준화가 힘들어 보이는 문제점이 있다

1544709183947

이 부분을 해결하기 위해서 새로운 함수를 적용시켜 보자.

  • StandartDeviation : 표준편차

1544709363419

moments

tensorflowjs 에는 함수를 제공 한다. => moments

예시를 들어보자.

const numbers = tf.tensor([
	[1, 2],
  [3, 4],
  [5, 6]
]);

tf.moments(numbers);

....
{"mean":{"isDisposedInternal":false,"shape":[],"dtype":"float32","size":1,"strides":[],"dataId":{},"id":41,"rankType":"0"},"variance":{"isDisposedInternal":false,"shape":[],"dtype":"float32","size":1,"strides":[],"dataId":{},"id":50,"rankType":"0"}}

우리는 여기에서 mean 과 variance 를 사용할 수 있다.

  • average : mean

  • value : numbers

  • StandartDeviation : sqrt(variance)

1544709860334

const numbers = tf.tensor([
  [1, 2],
  [3, 4],
  [5, 6]
]);

// 0은 축을 바꿔서 계산한다.
const { mean, variance} = tf.moments(numbers, 0);

numbers.sub(mean).div(variance.pow(.5))

.........
[[-1.2247449, -1.2247449], [0 , 0 ], [1.2247449 , 1.2247449 ]]

이제 본 소스에 적용해보자.

function knn(features, labels, predictionPoint, k) {
    const { mean, variance } = tf.moments(features, 0);

    const scaledPrediction = predictionPoint.sub(mean).div(variance.pow(0.5))

    return features
        .sub(mean)
        .div(variance.pow(0.5))
        .sub(scaledPrediction)
        .pow(2)
        .sum(1)
        .pow(0.5)
        .expandDims(1)
        .concat(labels, 1)
        .unstack()
        .sort((a, b) => {
            a.get(0) > b.get(0) ? 1 : -1
        })
        .slice(0, k)
        .reduce((acc, pair) => acc + pair.get(1), 0) / k;
}
Error -15.323502304147466
Error -11.344580119965723
Error -2.047058823529412
Error 19.327433628318584
Error 7.806324110671936
Error -14.106372465729613
Error -8.782552083333334
Error 13.227406199021207
Error -36.336911441815076
Error 7.381578947368421

30프로정도 확률안에서 도는 정도까지 수정했다.

이 보다 정확도를 올리는 방법은 두가지가 있다.

  • testFeatures 수를 좀 더 수를 늘리거나
  • 훈련수를 늘리거나

이건 다음 알고리즘 수업을 통해서 공부를 해보자.

전체 소스

require('@tensorflow/tfjs-node');
const tf = require('@tensorflow/tfjs');
const loadCSV = require('./load-csv');

function knn(features, labels, predictionPoint, k) {
  const { mean, variance } = tf.moments(features, 0);

  const scaledPrediction = predictionPoint.sub(mean).div(variance.pow(0.5));

  return (
    features
      .sub(mean)
      .div(variance.pow(0.5))
      .sub(scaledPrediction)
      .pow(2)
      .sum(1)
      .pow(0.5)
      .expandDims(1)
      .concat(labels, 1)
      .unstack()
      .sort((a, b) => (a.get(0) > b.get(0) ? 1 : -1))
      .slice(0, k)
      .reduce((acc, pair) => acc + pair.get(1), 0) / k
  );
}

let { features, labels, testFeatures, testLabels } = loadCSV(
  'kc_house_data.csv',
  {
    shuffle: true,
    splitTest: 10,
    dataColumns: ['lat', 'long', 'sqft_lot', 'sqft_living'],
    labelColumns: ['price']
  }
);

features = tf.tensor(features);
labels = tf.tensor(labels);

testFeatures.forEach((testPoint, i) => {
  const result = knn(features, labels, tf.tensor(testPoint), 10);
  const err = (testLabels[i][0] - result) / testLabels[i][0];
  console.log('Error', err * 100);
});


'스터디 > Tensorflow JS' 카테고리의 다른 글

tensorflow js 입문 (스터디 3주차 정리)  (0) 2018.12.05

NodeJS 스터디 1주차 정리

부산에서 매주 진행되는 노드 입문 강좌 스터디입니다.

부산에서 다른 스터디 내용을 보실려면 카페 에서 보실수 있습니다.

이 강좌는 제로초님의 인프런 강좌를 보고 요약해놓은 내용입니다.

  • 챕터 2 - ES2018
  • 챕터 3 - 노드 기능 알아보기

const & let

const

const 는 객체가 할당된 경우 변경이 불가능하지만 오브젝트에 대한 내부 값은 변경가능하다.

const item = {a: 1, b: 2, c: 3};

item.a = 3;

item => {a: 3, b: 2, c: 3}

let

구문적인 변수 영역 규칙을 지원한다.

{} 블록내에 변수 영역만 처리된다.

예를 들어 보자.

var 로 할 경우 밖의 변수까지 변경되는 걸 볼수 있다.

var test = "test";

if (test) {
    var test = "test2";
    console.log('블록내 : ',test);
}

console.log('블록밖', test);

.....
블록내 : test2
블록밖 test2

하지만 let 으로 할 경우 글로벌 변수의 값을 보호할 수 있다.

var test = "test";

if (test) {
    let test = "test2";
    console.log('블록내 : ',test);
}

console.log('블록밖 : ', test);

......
블록내 :  test2
블록밖 :  test

템플릿 문자열

템플릿 문자열은 문자열 연결 대신 사용할 수 있다. 중간에 변수를 삽입할수 있다.

${변수}, ` 로 감싼다.

console.log(test + " " + test2); //기존 방식

console.log(`${test} ${test2}); //템플릿 문자열

객체 리터럴

화살표 함수 (중요)

화살표를 사용하면 모든 함수 정의를 한줄로 끝낼수 있다. function 키워드를 없앴고, 화살표가 어떤 값을 반환하는지 지정해주기 때문에 return 도 없다. 만약 파라미터가 하나만 받는 경우 괄호도 생략이 가능하다.

화살표 함수는 this를 새로 바인딩 하지 않는다. 예를 들어 다음 코드에서 this는 city객체가 아니다.

const city = {
    areas: ["서울","부산"],
    print: function(delay = 1000) {
        setTimeout(function() {
            console.log(this.areas.join(','))
        }, delay);
    }
}

city.print();

....error
Cannot read property 'join' of undefined

이 경우 this는 window 객체이기 때문에 city 는 undefined 이다.

화살표 함수는 함수 내부의 this를 외부의 this와 같게 만든다.

const city = {
    areas: ["서울","부산"],
    print: (delay = 1000) => {
        setTimeout( () => {
            console.log(this.areas.join(','))
        }, delay);
    }
}

city.print();

....error
Cannot read property 'join' of undefined

위 코드에서 this도 결국에는 window 의 객체이기 때문에 오류가 난다.

그래서 상단의 print 함수를 function 으로 만들어주면 오류가 발생되지 않는다.

const city = {
    areas: ["서울","부산"],
    print: function(delay = 1000){
        setTimeout( () => {
            console.log(this.areas.join(','))
        }, delay);
    }
}

city.print();

.....error
서울,부산

구조분해 ( destructuring )

구조분해를 사용하면 객체 안에 있는 필드 값을 원하는 변수에 대입할 수 있다.

비구조화 할당 시 this가 의도와 다르게 동작하는 현상이 있을수 있는 점 유의하자.

var city = {
    area: ['busan','seoul'],
    latlng: ['123:456', '789:321']
}

const { area, latlng } = city;

console.log(area[0], latlng[0]);
...

const [ busan, seoul ] = area;

console.log( busan, seoul );

위 코드 처럼 배열도 비구조화 할당이 가능하다.

스프레드 연산자

기존 배열의 값은 변경하지 않고 새로 만들어서 리턴한다.

파라미터 갯수를 동적으로 받을수 있다.

const x = (a, ...b) => console.log(a,b);

x(1,2,3,4,5);

........
1 [ 2, 3, 4, 5 ]

Promise

비동기적인 동작을 잘 다루기 위한 방법 중 하나이다.

const promise = new Promise((resolve, reject) => {
    const a = 1;
    const b = 1;
    if(a + b <= 2) {
        resolve(a + b);
    } else {
        reject(a + b);
    }
});

promise
    .then(( success ) => {console.log(success)})
    .catch((error) => {console.log(error)});


.... 성공
> 2

또한 여러개의 then으로 사용가능하다. 콜백안에 다시 콜백이 있을 경우 여러개의 then 으로 이어진다. 그러기 위해선 resolve로 리턴을 해야줘야 한다.

const promise = new Promise((resolve, reject) => {
    const a = 1;
    const b = 1;
    if(a + b <= 2) {
        resolve(a + b);
    } else {
        reject(a + b);
    }
});

promise
    .then(( success ) => {
        return new Promise((resolve, reject) => {
            resolve("result : " + success);
        })
    })
    .then((success2) => console.log(success2))
    .catch((error) => {console.log("reject:" + error)});

....
result : 2

Promise API

무조건 성공 또는 실패 하는 경우일 경우 더 줄여서 가능하다.

const success = Promise.resolve('success');    
const failed = Promise.reject('failed');

Promise 함수들을 모아서 하나의 Promise 로 실행할수 있다.

전부 성공시 success 로 넘어가지만 한개라도 실패시 catch 로 들어가는 점 유의하자.

Promise.all([promise, success])
    .then((results) => {
        console.log(results);
    })
    .catch((error) => {
        console.log(error);
    })

Async / Await

Async 도 결국 내부에는 Promise 기반이므로 Promise를 꼭 습득하고 가자.

Promise의 단점은 결국 콜백함수내에서 이루어지기 때문에 외부 코드들과 연동성이 비동기로 이루어진다. 그래서 그걸 극복하게 동기형으로 바꿔주는 게 Async/Await 이다.

check = async () => {
    console.log('await result: ',await promise);
}

check();

오류 처리는 try / catch 문으로 한다.

check = async () => {
    try {
        console.log('await result: ',await promise);
    } catch(e) {
        console.error('error: ',e)
    }
}

check();

모듈

자바스크립트 모듈은 자바스크립트 파일에서 쉽게 불러와서 활용할 수 있는 재사용 가능한 코드 조각들을 말한다. 즉 쪼개서 불러와서 사용이 가능하다는 것이다.

자바스크립트는 각각의 모듈을 별도의 파일로 저장한다. 모듈을 만들고 외부에 익스포트 하는 방법에는 한 모듈에서 여러 자바스크립트 객체를 외부에 노출시키는 방법과 모듈당 하나의 자바스크립트 객체를 노출시키는 방법이 있다.

여러 객체를 외부에 노출 시키는 방법

>> helper.js

export const print = (message) => log(message, new Date())

export const log = (message, timestamp) => console.log(`${timestamp.toString()}: ${message}`)

module.exports = {
    print,
    log
}
const { print } = require('./txt_helper');

print("good");

하나의 객체를 외부에 노출 시키는 방법

const print = (message) => console.log(message, new Date())

export default print;
import print from './txt_helper_only_one.mjs';

print("good");

global

노드의 전역 객체

노드 실행환경내 전역에서 공통적으로 사용되는 객체이다.

> B.js

module.exports = () => global.message;
> C.js

const B = require('./B');

global.message = 'ok';

console.log(B());

>> ok 출력

console

console 객체네는 디버깅을 도와주는 많은 메소드가 준비되어 있다.

  • console.time('인자') & console.timeEnd('인자') - 전체 걸리는 시간을 측정해준다. (인자는 똑같아야 한다)
  • dir: 오브젝트 객체를 로깅할때 사용한다. colors 는 색상 지정, depth 는 깊이 지정
  • trace: 에러 발생시 오류 경로를 추척해서 보여준다.

setTimeout, setInterval, setImmediate

https://nodejs.org/ko/docs/guides/timers-in-node/

  • setTimeout : 일정시간 후에 콜백이 실행된다.
    • clearTimeout 로 취소 가능
  • setInterval : 일정 시간마다 콜백이 실행된다.
    • clearInterval로 취소 가능
  • setImmediate : 이벤트 루프 주기 끝에 코드를 실행한다.
    • clearImmediate로 취소 가능

__filename, __dirname

  • __filename 은 현재 파일 위치까지 위치를 리턴한다.
  • __dirname 은 디렉토리까지 위치를 리턴한다.

process

현재 실행중인 노드 프로그램 정보가 들어있다.

  • process.version : 노드 설치 버전
  • process.arch : 32비트인지 64비트인지 체크
  • process.platform : 운영체제
  • process.pid : 프로세스 아이디 ( 문제가 발생시 강제 종료시 아이디 지정 가능 )
  • process.uptime : 프로세스 실행되고 얼마나 지났는지 체크
  • process.cwd : 노드 프로그램이 실행되고 있는 위치
  • process.exePath : 노드 설치 위치
  • process.cpuUsage : cpu 사용량
  • process.exit : 프로세스 종료

OS

운영체제 관련된 모듈

  • os.cpu 의 경우는 멀티 프로세싱에서 자주 쓰인다.

Path

경로 관련된 모듈. **중요한 모듈중 하나이다. **

  • path.delimiter : 환경변수 패스 출력
  • path.sep : 경로 구분자 표시 . 윈도우는 \\ 이다.
  • path.dirname : 경로 폴더
  • path.extname : 확장자
  • path.basename : 파일 이름
  • path.parse : 구조로 분해
  • path.format : 분해된 구조를 합쳐준다.
  • path.normalize : 잘못된 경로를 자동으로 정상적으로 만들어준다.
  • path.isAbsoulte : 경로가 절대경로인지 상대경로인지 체크
  • path.join : 절대 경로 무시하고 조각나있는 경로를 합쳐준다.
  • path.resolve : 절대 경로 고려해서 합친다. 루트는 C:\

Url

도메인이 생략된 경우는 parse 를 통해서 처리

  • parse
  • format
  • searchParams : 파라미터 내용을 오브젝트 형태로 가져온다.
    • 파라미터들을 추가,수정,삭제등 할수 있다. (append,set,delete,get)

url 구조� �� �미� ��결과

querystring

  • url 모듈과 같이 사용한다.
  • url.parse ->querystring.parse 로 이용
  • stringify 는 파싱된 걸 다시 합쳐준다.

crypto

단방향 암호화(해시)
  • 비밀번호를 암호화할때 필요하다.

  • 복호화는 안된다.

  • createHash 는 비밀번호가 짧을 수록 같은 값이 나올수 있다.

  • 좀 더 나은 암호화 방식인 pbkdf2 함수를 사용

    • salt 라는 문자열을 비밀번호에 추가해서 좀 더 암호화한다.
    • salt 는 암호화된 비밀번호와 같이 저장
    • iteration 은 1초정도가 걸릴때까지 올려주면 좋음
  • 실무에선 bcrypt 등이 사용된다.

crypto.createHash('sha512').update('password').digest('base64');
양방향 암호화 (복호화 가능)
  • 암호화시 createCipher 함수 사용
  • aes-256 또는 aes-256-cbc 를 많이 사용한다.
  • 복호화시 createDecipher 함수 사용
  • key 값을 알아야 복호화를 할 수 있다.
  • update 함수 사용시 base64,utf8 인자가 암호화와 복호화시 바뀐다.

Util

  • callbackify : promise 함수를 거꾸로 callback 함수로 바꿔준다.
  • promisifycallback 함수를 promise 로 바꿔주는 유틸 함수 (중요함)
  • deprecate : 폐기되는 함수임을 지정한다.
    • 오류 메세지도 인자로 넣을 수 있다.
    • 결과에 Warning 이 나온다.

Fs (File System)

  • 비동기 방식으로 콜백으로 결과로 받는다.
  • 여러개의 콜백을 지정시 순서가 일정하지가 않다.
const fs = require('fs');
fs.readFile('./readme.txt', (err, data) => {
    if(err) {
        throw err;
    }

    console.log(data); //바이너리 파일로 리턴된다. 
    console.log(data.toString());
});

.........
<Buffer ed 85 8c ec 8a a4 ed 8a b8 31 0d 0a ed 85 8c ec 8a a4 ed 8a b8 32>
테스트1
테스트2
  • 동기 방식으로는 sync 방식도 제공한다.
    • readFileSync

기타 함수

  • access
  • mkdir
  • open
  • rename
  • unlink : 파일 삭제
  • rmdir : 폴더 삭제

promise

  • 노드 10에서 promise사용가능

버퍼 ( Buffer ), 스트림 ( Stream )

  • 데이터를 보낼때 일정량의 저장 공간에(버퍼) 담아서 보낸다.
  • 버퍼들의 통로를 뜻함
const fs = require('fs');
const readStream = fs.createReadStream('./readme.txt', { highWaterMark: 8});
const data = [];

readStream.on('data', (chunk) => {
    data.push(chunk);
    console.log('data', chunk, chunk.length);
});

readStream.on('end', () => {
    console.log('end', Buffer.concat(data).toString());
});

readStream.on('error', (error) => {
    console.log('error', error);
})
data <Buffer ed 85 8c ec 8a a4 ed 8a> 8
data <Buffer b8 31 ed 85 8c ec 8a a4> 8
data <Buffer ed 8a b8 32 ed 85 8c ec> 8
data <Buffer 8a a4 ed 8a b8 31 ed 85> 8
data <Buffer 8c ec 8a a4 ed 8a b8 32> 8
data <Buffer ed 85 8c ec 8a a4 ed 8a> 8
data <Buffer b8 31 ed 85 8c ec 8a a4> 8
data <Buffer ed 8a b8 32 ed 85 8c ec> 8
data <Buffer 8a a4 ed 8a b8 31 ed 85> 8
data <Buffer 8c ec 8a a4 ed 8a b8 32> 8
data <Buffer ed 85 8c ec 8a a4 ed 8a> 8
data <Buffer b8 31 ed 85 8c ec 8a a4> 8
data <Buffer ed 8a b8 32 ed 85 8c ec> 8
data <Buffer 8a a4 ed 8a b8 31 ed 85> 8
data <Buffer 8c ec 8a a4 ed 8a b8 32> 8
data <Buffer ed 85 8c ec 8a a4 ed 8a> 8
data <Buffer b8 31 ed 85 8c ec 8a a4> 8
data <Buffer ed 8a b8 32 ed 85 8c ec> 8
data <Buffer 8a a4 ed 8a b8 31 ed 85> 8
data <Buffer 8c ec 8a a4 ed 8a b8 32> 8
data <Buffer ed 85 8c ec 8a a4 ed 8a> 8
data <Buffer b8 31 ed 85 8c ec 8a a4> 8
data <Buffer ed 8a b8 32 ed 85 8c ec> 8
data <Buffer 8a a4 ed 8a b8 31 ed 85> 8
data <Buffer 8c ec 8a a4 ed 8a b8 32> 8

청크별로 날라와서 end 이벤트 등록시 그 버퍼를 합쳐서 하나의 완전품으로 만든다.

WriteStream

const fs = require('fs');

const writeStream = fs.createWriteStream('./readme.txt');
writeStream.on('end', () => {
    console.log('완료')
})
writeStream.write('쓰기 완료1');
writeStream.write('두번째 줄');
writeStream.end();

Pipe

내부적으로 읽기와 쓰기를 연결해준다.

  • copy 함수가 따로 새로 만들어져서 사용가능하다.

zlib

압축을 할수 있다.

Event

미리 이벤트를 만들어 놓고 어떤 행동 발생시 콜백이 발생한다.

  • addListener
  • on : addListener 와 같지만 여러번 등록가능하다.
  • once : 한번만 실행된다.
  • listenerCount : 이벤트가 몇개 달려 있는지 갯수 확인
const EventEmitter = require('events');
const myEvent = new EventEmitter();

myEvent.addListener('visit', () => {
    console.log('welcome');    
});

myEvent.on('exit', () => {
    console.log('good bye');
});

myEvent.on('exit', () => {
    console.log('good bye please');
});

myEvent.once('special', () => {
    console.log('special event')
});

myEvent.emit('visit');
myEvent.emit('exit');
myEvent.emit('special');
myEvent.emit('special');

..........
welcome
good bye
good bye please
special event <-- 한번만 실행된걸 볼 수 있다. 

이벤트 취소

  • removeAllListeners(이름) 여러번 등록 했을 경우 다 제거
  • removeListener(이름, callback) 하나만 제거 가능하다. 어떤 걸 제거 해야 할지 알수 없으므로 콜백을 지정

예외처리

  • 노드는 싱글 쓰레드이므로 오류 발생시 전체에 영향을 끼칠수 있으므로 신중하게 처리
  • throw new Error
  • try / catch 로 처리 가능
  • async / await 도 try / catch
  • 내장 함수들은 error 발생해도 시스템이 중지되진 않는다.
  • process.on 으로 시스템 에러가 발생시 중지가 되지 않게 할 수 있다.
    • uncaughtExcepetion 으로 모든 에러를 잡을수 있다.
    • process.on 이벤트 실행이 꼭 보장되지 않는다.


이상으로 1주차 노드 스터디 내용 정리사항이었습니다. 

참석해주셔서 감사합니다. 

Tensorflow JS 입문 ( 부산 3주차 스터디 )

부산에서 매주 진행되는 스터디입니다.

부산에서 다른 스터디 내용을 보실려면 카페 에서 보실수 있습니다.

https://www.udemy.com/machine-learning-with-javascript 을 같이 보면서 공부중입니다.

목표

  • 텐서플로 JS 에 대한 이해
  • 텐서플로를 이용한 예제 재설계 ( lodash -> tensorflow)
  • tensorflow 를 이용해서 knn 알고리즘 재설계
  • tensorflow와 함계 다른 알고리즘 빌딩

저번주까지(2주차) 우리는 기본적인 머신러닝 기법인 KNN 에 대해서 공부했다.

공부한 내용은 다음과 같다.

1543958445494

공식 홈페이지

https://js.tensorflow.org/

1543959044497

첫번째 예제

tensorflow js를 이용해서 배열에 있는 숫자들을 작업하자

1543959200980

Dimensions

  • 1차원, 2차원, 3차원...n차원 배열

1543959388735

Shape

  • 각각의 배열에 얼마나 많은 레코드를 가지고 있는지 표현
  • 배열에 대한 길이를 뜻함

예를 들어 [5,10,17] 에 대한 shape 는 3 이라고 생각하면 된다.

1543959771631

1543959631283

1543959690992

1543959728153

실습

https://stephengrider.github.io/JSPlaygrounds/

const data = tf.tensor([1,2,3])

data.shape

>> [3]
const data = tf.tensor([1,2,3])
const otherdata = tf.tensor([4,5,6]);

data.add(otherdata);

>> [5,7,9]

1543960433839

const data = tf.tensor([1,2,3])
const otherdata = tf.tensor([4,5,6]);

data.sub(otherdata);
data.mul(otherdata);


>> [-3, -3, -3]
>> [4, 10, 18]
const data = tf.tensor([
  [1,2,3],
  [4,5,6]
]);
const otherdata = tf.tensor([
  [4,5,6],
  [1,2,3]
]);

data.add(otherdata);

>> [[5, 7, 9], [5, 7, 9]]

1543960846449

행렬의 길이가 안맞는 경우

지금까지 같은 배열을 계산을 진행했다. 하지만 만약 길이가 다른 경우에는 어떻게 처리할까?

1543960682971

const data = tf.tensor([1,2,3]);
const otherdata = tf.tensor([4]);

data.add(otherdata);

>> [5,6,7]

브로드캐스팅

행렬의 길이가 다른 경우에도 동작을 하기 위해선 조건이 있다. 그에 대해서 알아보자.

  • 오른쪽에서 왼쪽으로 이동되며 shapes 길이가 똑같거나 어느 한쪽이 1이 되어야 한다.

1543961094501

어느 한쪽길이가 1이라서 아래 두개의 수식은 성립된다. by 브로드캐스팅

1543961191494

오른쪽부터 왼쪽으로 1이 있으므로 계산 가능하다.

1543961320616

다른 텐서들중 하나가 없더라도 다른게 일치한다면 계산이 가능하다.

1543961420823

1543961530424

텐서플로 핸들링

Get

const data = tf.tensor([10, 20, 30]);

data.get(0);

>> 10

2차원 배열일 경우에는

1543961845376

const data = tf.tensor([
  [10, 20, 30],
  [40, 50, 60],  
]);

data.get(0, 1);

>> 20

값을 가져올때 중요한 점은 (row, column) 으로 가져온다는 것이다.

Set

const data = tf.tensor([
  [10, 20, 30],
  [40, 50, 60],  
]);

data.set(0, 0, 50) //작동안됨

Slice

const data = tf.tensor([
  [10, 20, 30],
  [40, 50, 60],
  [10, 60, 30],
  [10, 70, 30],
  [10, 80, 30],
  [10, 90, 30],
  [10, 100, 30],
  [10, 110, 30],
]);

data.slice([0,1], [8, 1])

>> [[20 ], [50 ], [60 ], [70 ], [80 ], [90 ], [100], [110]]

1543962708670

size 는 시작위치가 1이다.

....

data.slice([0,1], [-1, 2]) // 전체 길이를 선택시 -1로 가능하다. 

Concat

const tensorA = tf.tensor([
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9],
]);

const tensorB = tf.tensor([
  [10, 11, 12],
  [13, 14, 15],
  [16, 17, 18],
]);

tensorA.concat(tensorB);


>> [[1 , 2 , 3 ], [4 , 5 , 6 ], [7 , 8 , 9 ], [10, 11, 12], [13, 14, 15], [16, 17, 18]]

이 결과에서 만약 shape를 구하면 어떻게 될까?

tensorA.concat(tensorB).shape

>> [6,3]

???? 어떻게 이런 결과가 나올까?

처음 단계를 살펴보면 A,B 를 합치는 경우

1543963264350

이런 형태를 추측 할 수 있다.

하지만 shape 결과로 나온걸 추측해보면

1543963362289

이렇게 구성된 걸 볼 수 있다.

왜 이렇게 되는 건지 살펴보자.

Axis

텐서플로를 축으로 shape 계산이 가능하다.

기본 세로축은 0, 가로축은 1 로 계산한다.

1543963457162

가로축 기준으로 생각해보면

1543963528907

const tensorA = tf.tensor([
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9],
]);

const tensorB = tf.tensor([
  [10, 11, 12],
  [13, 14, 15],
  [16, 17, 18],
]);

tensorA.concat(tensorB,0).shape
tensorA.concat(tensorB,1).shape

>> [6,3]
>> [3,6]

실제 예제

만약 멀리뛰기를 예를 들어보자.

1543963712867

const jumpData = tf.tensor([
  [70, 70, 70],
  [80, 70, 90],
  [70, 70, 70]
]);

const playerData = tf.tensor([
  [1,160],
  [2,160],
  [3,160],
  [4,160],
]);

jumpData.sum(0)
jumpData.sum(1)

>> 
[220, 210, 230] //세로축 기준
[210, 240, 210] //가로축 기준

두개의 shape 가 안맞아서 에러가 난다.

const jumpData = tf.tensor([
  [70, 70, 70],
  [80, 70, 90],
  [70, 70, 70]
]);

const playerData = tf.tensor([
  [1,160],
  [2,160],
  [3,160],
  [4,160],
]);

jumpData.concat(playerData)

>> 에러

Error: Error in concat2D: Shape of tensors[1] (4,2) does not match the shape of the rest (3,3) along the non-concatenated axis 1.

이럴 경우 해결 방법에 대해서 알아보자.

const jumpData = tf.tensor([
  [70, 70, 70],
  [80, 70, 90],
  [70, 70, 70],
  [70, 70, 70]
]);

const playerData = tf.tensor([
  [1,160],
  [2,160],
  [3,160],
  [4,160],
]);

jumpData.sum(1, true).concat(playerData, 1)

>> [[210, 1, 160], [240, 2, 160], [210, 3, 160], [210, 4, 160]]

expandDims

const jumpData = tf.tensor([
  [70, 70, 70],
  [80, 70, 90],
  [70, 70, 70],
  [70, 70, 70]
]);

const playerData = tf.tensor([
  [1,160],
  [2,160],
  [3,160],
  [4,160],
]);

jumpData.sum(1).expandDims(1)

>> [[210], [240], [210], [210]]
const jumpData = tf.tensor([
  [70, 70, 70],
  [80, 70, 90],
  [70, 70, 70],
  [70, 70, 70]
]);

const playerData = tf.tensor([
  [1,160],
  [2,160],
  [3,160],
  [4,160],
]);

jumpData.sum(1).expandDims(1).concat(playerData, 1)

>> [[210, 1, 160], [240, 2, 160], [210, 3, 160], [210, 4, 160]]

여기까지 3주차 스터디 내용이었다.

주로 배운 내용은 텐서플로 JS 에서의 이해였다.

참석해주셔서 감사합니다.

'스터디 > Tensorflow JS' 카테고리의 다른 글

Tensorflow JS 스터디 4주차  (0) 2018.12.13

+ Recent posts