블로그 이미지
윤영식
Full Stacker, Application Architecter, KnowHow Dispenser and Bike Rider

Publication

Category

Recent Post

2018. 11. 30. 15:48 Meteor/React + Meteor

React의 route 설정을 리팩토링해보고, createRef() 를 사용하지 않고 form을 변경해 본다. 


  - react-router v4에 맞는 redux 환경 재구성

  - react-final-form  적용하기

  - 사용자별 Link 목록 보여주기

  - 적용소스




React Router v4에 맞는 Redux 설정


react-router-redux는 react router v2와 v3에 맞는 패키지이고 react router v4에 맞는 connected-react-router로 교체해야 한다. recct-router-redux 설정 상태로는 redux action이 작동하지 않는다.

// 설치

$ meteor npm install --save connected-react-router

// 삭제

$ meteor npm uninstall --save react-router-redux

$ meteor npm uninstall --save @types/react-router-redux


imports/ui/store.ts 리팩토링

  - Routes.tsx의 browserHistory 를 store.ts로 옮김

  - connectRouter로 routerReducer를 생성

  - RouterAction, LocationChangeAction을 추가

import { createBrowserHistory } from 'history';

import { connectRouter, RouterAction, LocationChangeAction } from 'connected-react-router';


export const browserHistory = createBrowserHistory();


const rootReducer = combineReducers({

  router: connectRouter(browserHistory),

  links: linkReducer

});


export type RootState = StateType<typeof rootReducer>;


type ReactRouterAction = RouterAction | LocationChangeAction;

export type RootAction = ReactRouterAction | LinkAction;


imports/ui/Routes.tsx 리팩토링

  - react-router v4의 Router를 사용하지 않고, connected-react-router의 ConnectedRouter를 사용

  - 인자로 store.ts 에서 생성한 browserHistory를 사용

import { ConnectedRouter } from 'connected-react-router';

import store, { browserHistory } from './store';


export const Root = (

  <Provider store={store}>

    <ConnectedRouter history={browserHistory}>

      <Switch>

        <Route exact path="/" component={Login} />

        <Route path="/main" component={App} />

        <Route path="/signup" component={Signup} />

        <Route path="/links" component={InfoContainer} />

        <Route path="*" component={NotFound} />

      </Switch>

    </ConnectedRouter>

  </Provider>

);


이제 Redux Chrome Extension에서 LOCATION_CHANGE 액션을 볼 수 있다. 




final-form 통해 입력


form 관련부분을 final-form으로 변경한다. React버전의 react-final-form을 사용한다. 

$ meteor npm install --save final-form 

$ meteor npm install --save react-final-form


로그인 화면부터 react-final-form으로 변경해 본다. 

  - react-final-form의 Form, Field import

  - Form 의 onSubmit={this.onLogin}을 통해 입력한 values 객체를 받기, 보통 폼 필드의 name을 key로하여 json 객체를 받는다. 

  - <input> 태그를 <Field> 태그로 변경

  - <button> 태그에 disabled 속성추가

  - 필요없는 부분 추석처리: React.createRef()

import * as React from 'react';

import { Link } from 'react-router-dom';

import { Meteor } from 'meteor/meteor';

import { Form, Field } from 'react-final-form';


export interface LoginProps {

  history: any;

}


export interface LoginState {

  error: string;

}


export default class Login extends React.Component<LoginProps, LoginState> {

  // email: any = React.createRef();

  // password: any = React.createRef();


  constructor(props) {

    super(props);

    this.state = {

      error: ''

    };

  }


  onLogin = ({email, password}) => {

    // e.preventDefault();

    // let email = this.email.current.value.trim();

    // let password = this.password.current.value.trim();

    if (!email || !password) {

      this.setState({error: 'Please input email and password both'});

      return;

    }

    Meteor.loginWithPassword({ email }, password, (err) => {

      if (err) {

        this.setState({ error: err.reason });

      } else {

        this.setState({ error: '' });

      }

    });

  }


  makeForm = ({ handleSubmit, submitting, pristine, values }) => {

    return (

      <form onSubmit={handleSubmit}>

        <Field name="email" component="input" type="email" placeholder="Email" required/>

        <Field name="password" component="input" type="password" placeholder="Passowrd"/>

        {/* <input type="email" ref={this.email} name="email" placeholder="Email" />

        <input type="password" ref={this.password} name="password" placeholder="Password" /> */}

        <button type="submit" disabled={submitting || pristine}>Login</button>

      </form>

    );

  };


  public render() {

    return (

      <div>

        <h1>Login to short Link</h1>

        {this.state.error ? <p>{this.state.error} </p> : undefined}

        <Form onSubmit={this.onLogin} render={this.makeForm} />

        <Link to="/signup">Have a account?</Link>

      </div>

    );

  }

}


Signup.tsx, AddLink.tsx 도 변경한다. 




사용자별 Link 목록 보여주기


사용자를 추가하여 각 사용자가 등록한 목록만 보기 위해서 pub/sub 설정에서 userId  파라미터를 넘겨주어 본인 목록만 조회하여 publish 한다. 

// imports/ui/Info.tsx 맨 하단의 subscribe시에 자신의 아이디를 파라미터로 보낸다. 

export default compose(

  withTracker(() => {

    const connection = Meteor.subscribe('links', {userId: Meteor.userId()});

    return {

      links: Links.find().fetch(),

      loading: !connection.ready()

    };

  }),

  connect(mapProps)

)(Info);


imports/api/links.ts에서 userId를 파라미터로 find한다. 

if (Meteor.isServer) {

  Meteor.publish('links', ({userId}) => {

    console.log('userId:', userId);

    if (!userId) {

      return this.ready();

    }

    return Links.find({owner: userId});

  });

  ...

}




<참조>

- connected-react-router 저장소

- react-final-form 소개 영상

posted by 윤영식
2018. 11. 28. 16:56 Meteor/React + Meteor

Meteor와 React 환경을 좀 더 Production에 가깝게 만들어 본다. 


  - Insecure 제거 및 allow/deny 설정

  - React의 Refs에 대해 변경

  - Meteor methods/call 사용한 목록 조회

  - SimpleSchema 적용

  - 적용소스




Insecure 패키지 제거 및 Allow/Deny 설정


모든 컬렉션에 대한 자동 subscribe에 대한 autopublish 패키지는 이미 제거를 했고, 다음으로 insecure 패키지를 제거하여 컬렉션의 접근 권한을 제어하는 allow/deny를 설정한다. 

$ meteor remove insecure


insecure를 제거하게 되면 read만 가능하고 update, delete, insert가 불가능하다. 따라서 권한부분은 서버에서 실행되기 때문에 allow/deny설정을 Meteor.isServer블럭안에서 해주어야 한다.

  - allow: true로 설정된 것만 가능하고 그외는 모두 불가능하다. (가능한 것이 적으면 allow 설정)

  - deny: true로 서정된 것만 불가능하고 그외는 모두 가능하다.  (불가능한 것이 적으면 deny 설정)

// api/links.ts

import { Meteor } from 'meteor/meteor';

import { Mongo } from 'meteor/mongo';


const Links = new Mongo.Collection('links');


if (Meteor.isServer) {

  Meteor.publish('links', () => Links.find());

  Links.allow({

    insert (userId: string, doc: any) {

      console.log('insert doc:', doc);

      return (userId && doc.owner === userId);

    },

    remove(userId: string, doc: any) {

     console.log('delete doc:', doc);

      return (userId && doc.owner === userId);

    }

  })

}

export default Links;


 allow에서 owner 아이디를 비교하므로 epic에서 owner 값을 설정한다. 

// imports/ui/pages/link/link.epic.ts

const addLink: Epic = (

  action$,

  store

) =>

  action$.pipe(

    filter(isOfType(actions.ADD_REQUEST)),

    switchMap(action => {

      const { title, url } = action.payload;

      const owner = Meteor.userId();

      return insertCollection(Links, { title, url, owner, createdAt: new Date() })

    }),

  ...

);


userId는 로그인을 하게되면 이미 서버와 동기화 되어 서버가 가지고 있는 userId를 첫번째 파라미터로 넘겨준다. 두번째 파라미터 doc은 insert할 때 입력한 파라미터값 이고, remove의 doc은 저장된 몽고디비의 document이다. 

// meteor run을 수행한 콘솔창에 찍힌다. 

=> Meteor server restarted

I20181123-15:00:36.619(9)? insert doc: { title: 'google2',

I20181123-15:00:36.676(9)?   url: 'http://www.google.com',

I20181123-15:00:36.676(9)?   owner: 'AmMBJzZ33Nc8Bsqhe',

I20181123-15:00:36.676(9)?   createdAt: 2018-11-23T06:00:36.611Z }

I20181123-15:00:39.955(9)? remove doc: { _id: 'Zrb8jGSpt2AyRn4Q3',

I20181123-15:00:39.955(9)?   title: 'google2',

I20181123-15:00:39.955(9)?   url: 'http://www.google.com',

I20181123-15:00:39.955(9)?   owner: 'AmMBJzZ33Nc8Bsqhe',

I20181123-15:00:39.955(9)?   createdAt: 2018-11-23T06:00:36.611Z }




Form 입력의 React의 Refs 변경


React에서 DOM객체 접근 방법은 3가지 이고, string과  콜백펑션말고 16.3 버전이후 나온 createRefs()를 사용한다. 

  - ref="string"

  - ref={callback-function}

  - createRefs()


DOM 객체를 받을 변수를 선언하고, ref={this.변수}로 할당한다. 값 접근은 this.변수.current 객체를 통한다.

// imports/ui/pages/Login.tsx

.. 중략 ..

export default class Login extends React.Component<LoginProps, LoginState> {

  email: any = React.createRef();

  password: any = React.createRef();


  constructor(props) {

    super(props);

    this.state = {

      error: ''

    };

  }


  onLogin = (e: any) => {

    e.preventDefault();


    let email = this.email.current.value.trim();

    let password = this.password.current.value.trim();

    Meteor.loginWithPassword({ email }, password, (err) => {

      if (err) {

        this.setState({ error: err.reason });

      } else {

        this.setState({ error: '' });

      }

    });

  }


  public render() {

    return (

      <div>

        <h1>Login to short Link</h1>

        {this.state.error ? <p>{this.state.error} </p> : undefined}

        <form onSubmit={this.onLogin}>

          <input type="email" ref={this.email} name="email" placeholder="Email" />

          <input type="password" ref={this.password} name="password" placeholder="Password" />

          <button>Login</button>

        </form>

        <Link to="/signup">Have a account?</Link>

      </div>

    );

  }

}


Signup.tsx과 AddLink.tsx도 동일하게 변경한다. 




Meteor methods/call을 사용한 저장/삭제


imports/sdk/utils/ddp.util.ts 에 method call에 대한 observable 반환 메소드 추가

export function insertCall(methodName: string, params: any): Observable<RequestModel> {

  return from(new Promise((resolve, reject) => {

    Meteor.call(methodName, params, (error, result) => {

      if (error) {

        reject({ error: true, result: { ...error }, params: { ...params } });

      }

      if (typeof result === 'string' || typeof result === 'number') {

        resolve({ success: true, result, params: { ...params } });

      } else {

        resolve({ success: true, result: { ...result }, params: { ...params } });

      }

    });

  }));

}


export function removeCall(methodName: string, _id: string): Observable<RequestModel> {

  return from(new Promise((resolve, reject) => {

    Meteor.call(methodName, _id, (error, result) => {

      if (error) {

        reject({ error: true, result: { ...error }, params: { _id } });

      }

      if (typeof result === 'string' || typeof result === 'number') {

        resolve({ success: true, result, params: { _id } });

      } else {

        resolve({ success: true, result: { ...result }, params: { _id } });

      }

    });

  }));

}


imports/api/links.ts 에서 Meteor.methods를 추가한다. 

if (Meteor.isServer) {

 ... 

  Meteor.methods({

    insertLink(params: any) {

      if (!this.userId) {

        throw new Meteor.Error('Please login');

      }

      return Links.insert(params);

    },

    removeLink(_id: string) {

      if (!this.userId) {

        throw new Meteor.Error('Please login');

      }

      return Links.remove(_id);

    }

  });

}


imports/ui/pages/link/link.epic.ts 에서 collection 을 호출하지 않고, Meteor.call을 호출 한다. takeUntil은 브라우져 이동등을 할때 호출을 끊는 역할을 한다. 현재는 주석처리로 미구현상태임.

const addLink: Epic = (

  action$,

  store

) =>

  action$.pipe(

    filter(isOfType(actions.ADD_REQUEST)),

    switchMap(action => {

      const { title, url } = action.payload;

      const owner = Meteor.userId();

      return insertCall('insertLink', { title, url, owner, createdAt: new Date() })

      // return insertCollection(Links, { title, url, owner, createdAt: new Date() })

    }),

    map((response: RequestModel) => {

      if (response.error) {

        return actions.addLinkFailed({ ...response.result })

      }

      return actions.addLinkSuccess(response.result)

    }),

    // takeUntil(action$.pipe(

    //   filter(isOfType(actions.ADD_REQUEST))

    // ))

  );


const removeLink: Epic = (

  action$,

  store

) =>

  action$.pipe(

    filter(isOfType(actions.DELETE_REQUEST)),

    switchMap(action => {

      return removeCall('removeLink', action.payload);

      // return removeCollection(Links, action.payload);

    }),

    map((response: RequestModel) => {

      if (response.error) {

        return actions.removeLinkFailed({ ...response.result, ...response.params })

      }

      return actions.removeLinkSuccess(response.params._id);

    }),

    // takeUntil(action$.pipe(

    //   filter(isOfType(actions.ADD_REQUEST))

    // ))

  );




SimpleSchema 적용하기 


Validation을 위해 simple schema 패키지를 설치한다. 

$ meteor npm install --save simpl-schema


/imports/api/links.ts 에 schema를 정의한다. 

import { Meteor } from 'meteor/meteor';

import { Mongo } from 'meteor/mongo';

import SimpleSchema from 'simpl-schema';


const Links = new Mongo.Collection('links');


if (Meteor.isServer) {

 ...

  const linkSchema = new SimpleSchema({

    title: {

      type: String,

      min: 3

    },

    url: {

      type: String

    },

    owner: {

      type: String

    },

    createdAt: {

      type: Date 

    }

  });


  Meteor.methods({

    insertLink(params: any) {

      if (!this.userId) {

        throw new Meteor.Error('Please login');

      }

      try {

        linkSchema.validate(params);

        return Links.insert(params);

      } catch(e) {

        throw new Meteor.Error('no valid schema');

      }

    },

   ...

  });

}

export default Links;


account에 대한 validate도 정의해 본다. imports/api/ 폴더아래에 account-validate.ts 파일을 생성하고 server/main.ts에서 import한다. 

// account-validate.ts

import { Meteor } from 'meteor/meteor';

import { Accounts } from 'meteor/accounts-base';

import SimpleSchema from 'simpl-schema';


if (Meteor.isServer) {

  Accounts.validateNewUser((user) => {

    const email = user.emails[0].address;


    try {

      new SimpleSchema({

        email: {

          type: String,

          regEx: SimpleSchema.RegEx.Email

        }

      }).validate({ email });

    } catch(e) {

      throw new Meteor.Error(400, e.message);

    }


    return true;

  });

}


// server/main.ts

import { Meteor } from 'meteor/meteor';

import '../imports/api/links';

import '../imports/api/account-validate';


Meteor.startup(() => {

});


// Singup.tsx 파일에 테스트를 위해 noValidate를 추가한다. 

<form onSubmit={this.onCreateAccount} noValidate>




<참조>

React createRefs() 사용하기

- Meteor methods/call 사용하기

posted by 윤영식
2018. 11. 26. 16:05 BlockChain

2018 W3C 컨퍼런스를 요약한다. 관심분야는 Web3.0, WebRTC, Game, BlockChain 이다. 물론 경품도...




Web 3.0


web2.0 시대에 사용자의 데이터를 가지고 정보로 가공하여 장사를 하고 있다. 플랫폼 독점 기업의 특징. 예) 구글, 페이스북


탈중앙화된 웹의 필요성

  - 데이터 정보 제공자에게 더 많은 인센티브를 줘야 한다. 

  - 사용자가 정보의 주인

  - 거대 플랫폼으로부터 자유로운

  - 장애와 공격으로 부터 자유로운


데이터의 탈중앙화가 목표

  - 데이터가 서비스 업체의 저장소에 있는 것이 아니라 프로토콜 내에 있는 것이다. 

  - 여러 블록체인 서비스는 새로운 프로토콜의 서비스이다. 예) 스팀잇

  - 구조의 변경을 의미하고 팀버너스리가 Web3.0을 주창하고 있다.


변경되는 점

  - 프로토콜 개발은?

  - 애플리케이션 개발은?j


libp2p

  - CASTO: 리모트몬스터에서 하고 있는 서비스: live stream broadcast를 위한 decentralized web

    MiniStreamer 소스코드

  - IPFS 정의




CASTO


분산화 방송 플랫폼 코어 기술을 만들고 있음.

  - 블록체인: 서버 + DB + 분산시스템 + 보상시스템

  

이더리움선택

  - geth

  - remix: 웹 IDE 

  - solidity: 개발 언어

  - web3.js: geth 접근, JSONRPC 사용

  - metamask: web3.js 포함, 이더리움 월렛 역할, 사용자 Auth(컨트랙트 실행권한)  

    => 로컬 개발 내역을 웹으로통해 geth node로 접근할 때 반드시 필요하다. 

    => Ganache 사이트 참조


메타마스크

  - 소스분석

  - 소스의 web3는 메타마스크가 주입해 준것이다. 

  - 신규는 비동기 async await 사용 가능 (구버전은 callback방식)

// https://github.com/MobiconSoft/simple-blog-example-on-ethereum/blob/master/src/index.js

let web3js = new Web3(web3.currentProvider);




Serverless P2P 구축



- IPFS의 근간이 되는 libp2p 라이브러리를 근간으로 사용하는 애플리케이션일뿐...

- libp2p 브라우져, Node.js, Javascript 지원

- 연결과정

  + discover: peer 끼리 dial 

  + 연결받은 peer가 handle을 상대 peer에게 전달한다. 

- mutliAddr를 제공하여 접속

  + 서로 다른 네트워크 환경이어도 접근이 가능하다. 

- lib2p2는 TCP, WebRTC, WebSockets, UDP를 제공한다.

- Node 장애에서도 무정지 버시스 대응이 가능하다.

// 로컬 노드에서 수행하기 (소스)

$ npm install -g libp2p-webrtc-star

$ start-signal --port 12345


- 만일 electron등의 애플리케이션으로 수행을 하면 해당 애플리케이션 WebRTC server 즉, Node역할을 할 수 있다. 




HTML5 기반 블록체인 서비스


WebRTC + Blockchain 기반의 웹 게임 서비스의 개발: PlayDapp

  - 블록체인에서 웹, WebRTC가 굉장히 중요해진다. 

  - 통화로: 비트코인, 캐시, 이더리움, EOS, 리플만 사용


사용이유

  - 고객의 데이터로 이익창출이지만 고객에게 돌아가는 보상이 없다.

  - 데이터 중앙집중 관리로 해킹 공격 대상

  - 서비스의 영속성 문제: 경영 문제시 서비스 Shutdown


web + p2p + blockchain 을 통한 platform의 확산이 예상됨.




<참조>

- 블록체인이 불러온 Web3.0

- IPFS란 무엇인가?

- Awesome IPFS 목록

- 이재호님 Serverless libp2p 발표 슬라이드


posted by 윤영식
2018. 11. 25. 18:05 AI Deep Learning

자연어 처리 강좌를 정리한다. 





개념


- 문장을 분해하여 관계를 만들어 주어야 한다. 

- 데이터를 서로 연결하다보면 2차원 이상으로 발전한다. 

- 자연어는 모델링은 쉽지만, 전처리와 결과의 해석이 중요한다. 결과에 따른 해석이 따라 의미가 틀려진다. 이는 이미지/음성과 틀린 부분이다.

- 음운론, 형태론, 통사론, 의미론, 추리론

- Document -> Tokenizing -> Streaming/Tagging (컴퓨터가 이해하는 방식으로 배치하기) -> 최종 Word2Vec 에서 사용함




Tokenizing


- 구글 Colab을 사용한다.

- Token: document에서 쓸만한 것들로 의미를 같는 문자열 -> Tokenizing

- Colab에서 필요한 모듈 설치하기 in ubuntu

! apt-get update

! apt-get install g++ openjdk-8-jdk 

! pip3  install  nltk konlpy wordcloud matplotlib gensim 


! apt-get install fonts-nanum*

! apt-get install fontconfig

! fc-cache -fv

! cp /usr/share/fonts/truetype/nanum/Nanum* /usr/local/lib/python3.6/dist-packages/matplotlib/mpl-data/fonts/ttf/

! rm -rf /content/.cache/matplotlib/*


- import nltk # Natural Language ToolKit 사용

  + sent_tokenize, word_tokenize

- import re  #정규표현식 사용

// 패키지 사용

text = """갤럭시(GalaxyNote9)노트9. 2018년 08월 폭발적인 인기를 이끌고 있습니다. 

담당자 010-222-9999. 홍보팀 010-8888-9999"""

from nltk import sent_tokenize, word_tokenize, FreqDist

sent_tokenize(text)

tokens = word_tokenize(text)


// 정규표현식

import re

tokenizer = re.compile(r'[가-힣]+')

tokenizer.findall(text)




Stemming & Tagging


순서 Tokenizing -> Stemming -> Tagging을 한다. 일반/평문이 결과값이 잘나오고, 강조문/도치문/압축문은 일반/평문으로 만들어하는게 좋다. 


Stemming: 정규화

- 토큰의 어근/어간 추출

- 가능한 비슷한 의미 형태로 묶어주기 => 원형화한다

   예) 가다, 가니, 가고 => 가- 

         산뜻하다, 산뜻하니, 산뜻하고 => 산뜻하-


Tagging: 문법/Filter

- Token별 높은 확률의 태그(속성값/문법)을 추가 

- 단어의 품사별로 묶음: 동사, 명사, 부사


// 토큰

text = "Don't hesitate to ask question"

from nltk.tokenize import TreebankWordTokenizer

tokenizer = TreebankWordTokenizer()

token = tokenizer.tokenize(text)


// 태그

from nltk import pos_tag

pos_tag(token)


// 한글처리

// 테크

from konlpy.tag import Okt

twitter = Okt()

twitter.pos('서울R&D캠퍼스 수업자료')


//토큰, 스테밍을 같이 처리 stem === stemming

text = "워런 버핏은 삼성전자가 아닌 애플주식을 왜 샀을까"

print(twitter.pos(text, stem="true"))




Word Cloud로 시각화하기


- Visualization (시각화)

- 연설문을 워드클라우드로 표현하기 

// 필요 모듈 설치

! apt-get update

! apt-get install g++ openjdk-8-jdk 

! pip3  install  nltk konlpy wordcloud matplotlib gensim 


! apt-get install fonts-nanum*

! apt-get install fontconfig

! fc-cache -fv

! cp /usr/share/fonts/truetype/nanum/Nanum* /usr/local/lib/python3.6/dist-packages/matplotlib/mpl-data/fonts/ttf/

! rm -rf /content/.cache/matplotlib/*

speech_text = "https://raw.githubusercontent.com/YongBeomKim/nltk_tutorial/master/data/pyongyang_fin.txt"

script_text = "https://raw.githubusercontent.com/YongBeomKim/nltk_tutorial/master/data/movie_memories_of_murder_2003.txt"

font_file = "/usr/local/lib/python3.6/dist-packages/matplotlib/mpl-data/fonts/ttf/NanumGothicCoding.ttf"


// 워드클라우드 사용

import requests

import pandas as pd

import matplotlib.pyplot as plt

from wordcloud import WordCloud

from nltk import FreqDist

from nltk.tokenize import word_tokenize

import nltk

nltk.download('punkt')


texts = requests.get(speech_text).text

texts[:100]


// 그리기 

%matplotlib inline

wcloud = WordCloud(font_file).generate(texts)

plt.figure(figsize=(12,12))

plt.imshow(wcloud)

plt.axis('off')


명사만을 사용하여 word cloud 표현

from konlpy.tag import Okt

twitter = Okt()

tokens = twitter.pos(texts, stem=True)

tokens_noun = [token[0] for token in tokens if token[1] == 'Noun']

texts_noun = " ".join(tokens_noun)

texts_noun[:300]




Deep Learning 수행


Natural Language Processing + Deep Learning을 결합하여 챗봇을 만들어가기

  - RNN, LSTM, GRU가 챗봇만드는 기본 알고리즘

  - Data 입력/전처리 (Train Data) -> Cell 선택 및 모델 구성 -> Training / Modeling / Validation -> Model 평가 

  - Input -> Hidden Layer -> Output

  - Input Data 를 구성하는 Word2Vec


Word2Vec는 문장에 따라 데이터를 구조화한다. 

  - 하나의 중심데이터와 주변데이터로 구분함. 

  - 단어간 유사도 검사

  - 구조자체가 작아서, 딥러닝이라 할 수 없다. 

  - 로지스틱 회귀식(2 Cell)을 통해 분류한다. 

  - Corpus (말뭉치) -> Vocabulary Builder -> Context Builder -> Input -> Output 

  


새로운 방식

  - Input 하나에 Output 여러개를 통해 학습 => Skip-gram (sg)

  - try -> error를 통해 맞는 길로 찾아는 방식

과거 방식

  - Input 여러개에 대한 Output 으로 중심 단어 하나 => CBOW (Content Back of Word)


     



실습


  - 20차원의 구조를 만든다. 

  - Dimension(차원)안에 토큰을 넣는다. 비슷한 토큰은 같은 차원에 넣는다. 

  - 빈도수가 적은 것은 하나의 차원에 묶는다. 이안에 빈도수가 높은 것은 중심축에 놓는다. 

    



- size: 차원 30개 (중요)  <== 실습 단어가 151개여서 유의미화를 위해 30차원으로 줄여서 설정했음.

- window: 주변 데이터

- min_count: 10번이상 등장한 단어만 (중요)

- hs: ?

- workers: cpu parallel 동작

- sg: skip-gram

// 전처리후 

...

// 명사 저장

sentences = txtnoun(sentences, skip=skips, tags=["Noun"])

script_file = 'scripts.txt'

with open(script_file, 'w', encoding="utf-8") as file:

   file.write(sentences)


// gensim 이용 모델 만들기

%%time

from gensim.models import word2vec

data = word2vec.LineSentence(script_file)

model = word2vec.Word2Vec(data, size=30, window=2, min_count=10, hs=1, workers=4, iter=100, sg=1)

model_file = "script.model"

model.save(model_file)


결과

CPU times: user 1.59 s, sys: 113 ms, total: 1.71 s, Wall time: 1.7 s


// 모델의 단어수 

model = word2vec.Word2Vec.load(model_file)

len(model.wv.vocab.keys())

list(model.wv.index2word)


결과

151 


// 근접 백터값 확인하기, 참깨밭을 제거하고 추출해 봄

model.wv.most_similar(['현장','백광호'], negative=['참깨밭'], topn=20)


30차원을 시각화하기 위해 skitlearn의 TSNE를 이용해서 2차원으로 변경해서 표현한다. 




<참조>

- 강좌 슬라이드, 전체 실습 파일

- 정규 표현식 정리, 실습하는 서비스

KoNLP

- Word2Vec 관련 이론

posted by 윤영식
2018. 11. 22. 14:37 Meteor/React + Meteor

Meteor의  accounts-password를 패키지를 통한 로그인 환경 만들기를 해본다. 


  - accounts-password 패키지를 통한 미티어 사용자 가입/로그인 사용 방법 알기

  - React Router 설정

  - 미티어의 Tracker.autorun을 통해 reactivity computation과 resource 사용 방법 알기

  - 소스코드




미티어 사용자 관리


accounts-password 패키지를 설치하고, 가입 및 로그인 화면을 만든다. 

$ meteor add accounts-password

$ meteor npm install --save bcrypt

// Link 사용을 위해 

$ meteor npm install --save react-router-dom


Signup.tsx와 Login.tsx 생성한다. 

  - email, password의 값을 읽기 위하여 typescript방식의 public refs를 정의한다. 

  - Accounts.createUser를 통해 사용자를 등록한다.

// Singup.tsx

import * as React from 'react';

import { Link } from 'react-router-dom';

import { Accounts } from 'meteor/accounts-base'


export interface SignupProps {}

export default class Signup extends React.Component<SignupProps, any> {

  // @see https://goenning.net/2016/11/02/strongly-typed-react-refs-with-typescript/

  public refs: {

    email: HTMLInputElement,

    password: HTMLInputElement

  }


  constructor(props) {

    super(props);

    this.state = {

      error: ''

    };

  }


  onCreateAccount = (e: any) => {

    e.preventDefault();

    let email = this.refs.email.value.trim();

    let password = this.refs.password.value.trim();

    Accounts.createUser({email, password}, (err) => {

      if (err) {

        this.setState({ error: err.reason });

      } else {

        this.setState({ error: '' });

      }

    });

  }


  public render() {

    return (

      <div>

        <h1>Signup to short Link</h1>

        { this.state.error ? <p>{this.state.error} </p> : undefined}

        <form onSubmit={this.onCreateAccount}> 

          <input type="email" ref="email" name="email" placeholder="Email"/>

          <input type="password" ref="password" name="password" placeholder="Password"/>

          <button>Create Acount</button>

        </form>

        <Link to="/login">Already have a account?</Link>

      </div>

    );

  }

}


Login.tsx

  - refs 정의

  - Meteor.loginWithPassword를 통해 로그인한다.

import * as React from 'react';

import { Link } from 'react-router-dom';

import { Meteor } from 'meteor/meteor';


export interface LoginProps {

  history: any;

}

export default class Login extends React.Component<LoginProps, any> {

  public refs: {

    email: HTMLInputElement,

    password: HTMLInputElement

  }


  constructor(props) {

    super(props);

    this.state = {

      error: ''

    };

  }


  onLogin = (e: any) => {

    e.preventDefault();

    let email = this.refs.email.value.trim();

    let password = this.refs.password.value.trim();

    Meteor.loginWithPassword({ email }, password, (err) => {

      if (err) {

        this.setState({ error: err.reason });

      } else {

        this.setState({ error: '' });

      }

    });

  }


  public render() {

    return (

      <div>

        <h1>Login to short Link</h1>

        {this.state.error ? <p>{this.state.error} </p> : undefined}

        <form onSubmit={this.onLogin}>

          <input type="email" ref="email" name="email" placeholder="Email" />

          <input type="password" ref="password" name="password" placeholder="Password" />

          <button>Login</button>

        </form>

        <Link to="/signup">Have a account?</Link>

      </div>

    );

  }

}


NotFound.tsx 파일도 생성한다. 

import * as React from 'react';

export default () => <div>Not Found Page</div>




React Router 설정


라우팅 설정을 통해 가입, 로그인후 Link화면으로 이동하는 설정한다. 


history 모듈을 설치한다.

$ meteor npm install --save history


client/main.tsx에 Route설정을 한다.

import * as React from 'react';

import { Meteor } from 'meteor/meteor';

import { render } from 'react-dom';

import { Provider } from 'react-redux';

import { Route, Router, Switch } from 'react-router-dom';

import { createBrowserHistory } from 'history';

 

import App from '../imports/ui/App'

import Login from '../imports/ui/pages/Login';

import Signup from '../imports/ui/pages/Signup';

import InfoContainer from '../imports/ui/Info';

import NotFound from '../imports/ui/pages/NotFound';

import store from '../imports/ui/store';


const browserHistory = createBrowserHistory();

const Root = (

  <Provider store={store}>

    <Router history={browserHistory}>

      <Switch>

        <Route exact path="/" component={Login} />

        <Route path="/main" component={App} />

        <Route path="/signup" component={Signup} />

        <Route path="/links" component={InfoContainer} />

        <Route path="*" component={NotFound} />

      </Switch>

    </Router>

  </Provider>

);


Meteor.startup(() => {

  render(Root, document.getElementById('react-target'));

});


links는 인증된 사용자만 볼 수 있으므로 Tracker.autorun이라는 Reactivity Computation 영역에서 Meteor.user()라는 Reactivity Source가 로그인 상태인지 검사한다. 

  - 로그인 상태가 아니면 무조건 login화면으로 이동

  - 로그인 상태이면서 인증이 필요없는 화면인 경우 link화면으로 이동

// client/main.tsx 

import { Tracker } from 'meteor/tracker';


Tracker.autorun(() => {

  const isAuthenticated = !!Meteor.user();

  if (isAuthenticated) {

    const pathname = browserHistory.location.pathname;

    const unauthenticatedPages: any = ['/', '/signup'];

    const isUnauthenticatedPage = unauthenticatedPages.includes(pathname);

    if (isUnauthenticatedPage) {

      browserHistory.push('/links'); // main

    } else {

      browserHistory.push(pathname);

    }

  } else {

    browserHistory.push('/'); // login

  }

});


Meteor.startup(() => {

  render(Root, document.getElementById('react-target'));

});


imports/ui/Info.tsx 에 logout 기능 추가

...

import { Accounts } from 'meteor/accounts-base';


interface InfoProps {

  links: any;

  loading: boolean

}


class Info extends React.Component<InfoProps, any> {


  linkList() {

    const { links, loading } = this.props;

    if (loading) {

      return <div>loading...</div>

    } else {

      return <LinkList links={links} />

    }

  }


  onLogout = () => {

    Accounts.logout();

  }


  render() {

    return (

      <div>

        <button onClick={this.onLogout}>Log out</button>

        <AddLink />

        {this.linkList()}

      </div>

    );

  }

}




가입 및 로그인 테스트 확인하기


가입을 하고, 로그인을 한다. 

Chrome Extension 으로 MiniMongoExplorer를 설치하고 user Collection의 내용을 확인한다. 


또는 Chrome DevTool의 Application에서 로그인 사용자의 userId와 console에서 확인해 볼 수 있다. 또한 Link페이지에서 로그아웃을 해보고 콘솔을 확인해 본다. 




<참조>

- 크롬 확장툴 MiniMongoExplorer

- 세번째 글 소스

posted by 윤영식