spark-shell에서 PCA를 공부한다. (차원 축소)


차원 축소는 고려하는 변수의 수를 줄이는 과정이다. 원본과 잡음이 많은 피쳐에서 잠재 피쳐를 추출하거나 구조를 유지하면서 데이터를 압축하는 데 사용할 수 있다. 스파크 MLlib는 RowMatrix 클래스의 차원 축소를 지원한다. 데이터의 차원을 줄이기 위해 가장 일반적으로 사용되는 알고리즘은 PCA와 SVD이다. 


PCA를 살펴본다.



PCA는 가능한 상관 변수의 관찰 셋을 주성분(Principal component)라고 부르는 선형 무상관 변수 집합으로 변환하기 위해 직교(orthogonal) 트랜스포메이션을 사용하는 통계적 과정이다.



PCA 알고리즘은 PCA를 사용해 저차원 공간에 벡터를 투영하는 데 사용할 수 있다. 



그런 다음, 감소 된 피쳐 벡터에 기초해 ML 모델을 트레이닝할 수 있다. 


다음 예는 6차원 피쳐 벡터를 4 차원 주성분에 투영하는 방법을 보여준다. 다음과 같은 피쳐 벡터가 있다고 가정한다.


scala>
import org.apache.spark.ml.linalg.Vectors


val data = Array(

Vectors.dense(3.5, 2.0, 5.0, 6.3, 5.60, 2.4),

Vectors.dense(4.40, 0.10, 3.0, 9.0, 7.0, 8.75),

Vectors.dense(3.20, 2.40, 0.0, 6.0, 7.4, 3.34))





* 여기서 주의할 점은 Vectors의 패키지를 잘 확인해야 한다.


import org.apache.spark.mllib.linalg.Vectors이 아니라 import org.apache.spark.ml.linalg.Vectors을 사용해야 한다.




이제 데이터 프레임을 생성하자.


scala>


val df = spark.createDataFrame(data.map(Tuple1.apply)).toDF("features")

df.show(false)


+--------------------------+

|features                  |

+--------------------------+

|[3.5,2.0,5.0,6.3,5.6,2.4] |

|[4.4,0.1,3.0,9.0,7.0,8.75]|

|[3.2,2.4,0.0,6.0,7.4,3.34]|

+--------------------------+




이제 PCA에 대한 6차원 피쳐 벡터를 갖는 피쳐 데이터 프레임을 생성한다.


이제 다음과 같은 매개 변수를 설정하여 PCA 모델을 초기화한다.



import org.apache.spark.ml.feature.PCA


val pca = new PCA()

.setInputCol("features")

.setOutputCol("pcaFeatures")

.setK(4)

.fit(df)






차이를 주기 위해 setOutputCol 메소드를 사용하여 출력 컬럼을 pcaFeatures로 설정했다. 

그리고 PCA의 차원을 설정했다. 

마지막으로 트랜스포메이션을 생성하기 위해 데이터 프레임을 피팅했다. 



PCA 모델에는 explain variance(분산도)이 포함된다. 



val result = pca.transform(df).select("pcaFeatures")

result.show(false)


+-------------------------------------------------------------------------------+

|pcaFeatures                                                                    |

+-------------------------------------------------------------------------------+

|[-5.149253129088708,3.2157431427730376,-5.390271710168745,-1.0528214606325355] |

|[-12.372614091904456,0.8041966678176822,-5.390271710168748,-1.0528214606325337]|

|[-5.649682494292663,-2.1891778048858255,-5.390271710168744,-1.0528214606325337]|

+-------------------------------------------------------------------------------+



이전 코드는 PCA를 사용해 주성분으로서 4차원의 피쳐 벡터를 갖는 피쳐 데이터 프레임을 생성한다. 



Posted by '김용환'
,


python에 특이한 문법인 try-else문이 있어서 살펴본 예이다. 


except문이 실행되지 않으면 else 문이 실행된다. 

a=0
try:
a=1
except ZeroDivisionError as e:
print(str(e))
else:
print(a)


결과는 1이다.





다음은 일부러 0으로 나눠 ZeroDivisionError를 발생시키는 코드이다. 


except 문이 실행되면 else문이 실행되지 않는다. 

a=0
try:
a = 4/0
except ZeroDivisionError as e:
print(str(e))
else:
print(a)


결과는 다음과 같다.


division by zero




Posted by '김용환'
,


python에 특이한 문법인 for else가 있다고 해서 살펴봤다. 


for - else를 실행하는 예이다.


data = [1, 2, 3, 4, 5]
for i in data:
print(i)
else:
print("aa")

print("end")


결과는 다음과 같다.


1

2

3

4

5

aa

end






왜 필요할까. break와 연관된 문법이다.



중간에 break 문을 만나 for 문 바깥으로 나가면 else 문이 출력되지 않는다. 


data = [1, 2, 3, 4, 5]
for i in data:
print(i)
if i == 3:
break
else:
print("aa")

print("end")




결과는 다음과 같다.




1

2

3

end





Posted by '김용환'
,

spark-shell을 이용해 Spark의 StringIndexer 예이다.




StringIndexer는 레이블의 문자열 컬럼을 레이블 인덱스의 컬럼으로 인코딩한다. 


인덱스는 [0, numLabels]안에 있고 레이블 빈도에 따라 정렬되므로 


가장 빈번한 레이블은 0번째 인덱스를 갖는다. 



입력 컬럼이 숫자라면 숫자를 문자열로 변경하고 문자열 값을 인덱싱한다. 


에스티메이터( 또는 트랜스포머와 같은 다운 스트림 파이프 라인 컴포넌트가 문자열로 구성된 인덱스 레이블을 사용하는 경우 컴포넌트의 입력 컬럼을 문자열로 구성된 인덱스 컬럼 이름으로 설정해야 한다. 



대부분의 경우 setInputCol을 사용해 입력 컬럼을 설정할 수 있다. 다음처럼 포맷의 여러 범주 데이터가 있다고 가정하자.



이제 가장 빈번한 이름(이 경우 Jason)이 0번째 인덱스을 갖도록 name 컬럼을 인덱싱하고 싶다고 가정하자. 간단한 데이터 프레임을 생성한다.


scala>


val df = spark.createDataFrame(

Seq((0, "Jason", "Germany"),

(1, "David", "France"),

(2, "Martin", "Spain"),

(3, "Jason", "USA"),

(4, "Daiel", "UK"),

(5, "Moahmed", "Bangladesh"),

(6, "David", "Ireland"),

(7, "Jason", "Netherlands"))).toDF("id", "name", "address")



이제 다음과 같이 name 컬럼을 인덱싱하자.


import org.apache.spark.ml.feature.StringIndexer


val indexer = new StringIndexer()

.setInputCol("name")

.setOutputCol("label")

.fit(df)





이제 다음과 같이 StringIndexer 인스턴스에 트랜스포머를 사용한다.


val indexed = indexer.transform(df)




이제 올바로 동작하는지 살펴보자.


scala>

indexed.show(false)


+---+-------+-----------+-----+

|id |name   |address    |label|

+---+-------+-----------+-----+

|0  |Jason  |Germany    |0.0  |

|1  |David  |France     |1.0  |

|2  |Martin |Spain      |3.0  |

|3  |Jason  |USA        |0.0  |

|4  |Daiel  |UK         |4.0  |

|5  |Moahmed|Bangladesh |2.0  |

|6  |David  |Ireland    |1.0  |

|7  |Jason  |Netherlands|0.0  |

+---+-------+-----------+-----+


그림 13 : StringIndexer를 사용한 레이블 생성



결과를 보면 name을 기반으로 label의 값이 같다.




원-핫(one-hot) 인코딩은 레이블 인덱스 컬럼을 이진 벡터 컬럼으로 매핑하며 최대 값은 단일 값이다. 원-핫 인코딩을 사용하면 분류 피쳐를 사용하기 위해 로지스틱 회귀(Logistic Regression)와 같은 연속적인 피쳐를 기대하는 알고리즘을 사용할 수 있다



val df = spark.createDataFrame(

Seq((0, Array("Jason", "David")),

(1, Array("David", "Martin")),

(2, Array("Martin", "Jason")),

(3, Array("Jason", "Daiel")),

(4, Array("Daiel", "Martin")),

(5, Array("Moahmed", "Jason")),

(6, Array("David", "David")),

(7, Array("Jason", "Martin")))).toDF("id", "name")

df.show(false)


이제 데이터셋에서 가장 빈번한 이름(이 경우는 Jason이다)이 0번째 인덱스를 갖도록 name 컬럼을 인덱싱할 것이다. 


그러나 name 컬럼을 인덱스를 사용하는 것은 무엇일까? 



즉 name 컬럼을 추가로 벡터화할 수 있다면 데이터 프레임을 모든 머신 러닝 모델로 쉽게 제공할 수 있다.



StringIndexer로 피팅한 것을 기반으로 데이터 프레임 예를 변환한다. 


import org.apache.spark.ml.feature.StringIndexer

import org.apache.spark.ml.feature.OneHotEncoder


val indexer = new StringIndexer()

                 .setInputCol("name")

                 .setOutputCol("categoryIndex")

                 .fit(df)


val indexed = indexer.transform(df)




결과는 다음과 같다. 


scala> 


indexed.show(false)

+---+-------+-----------+-------------+

|id |name   |address    |categoryIndex|

+---+-------+-----------+-------------+

|0  |Jason  |Germany    |0.0          |

|1  |David  |France         |1.0          |

|2  |Martin |Spain          |3.0          |

|3  |Jason  |USA            |0.0          |

|4  |Daiel  |UK                |4.0          |

|5  |Moahmed|Bangladesh |2.0          |

|6  |David  |Ireland          |1.0          |

|7  |Jason  |Netherlands |0.0          |

+---+-------+-----------+-------------+





OneHotEncoder 인스턴스를 생성한다.


import org.apache.spark.ml.feature.OneHotEncoder

val encoder = new OneHotEncoder()

                 .setInputCol("categoryIndex")

                 .setOutputCol("categoryVec")





이제 트랜스포머를 사용해 벡터로 변환한다.


scala>

val encoded = encoder.transform(indexed)

encoded.show()


+---+-------+-----------+-------------+-------------+

| id|   name|    address|categoryIndex|  categoryVec|

+---+-------+-----------+-------------+-------------+

|  0|  Jason|    Germany|          0.0|(4,[0],[1.0])|

|  1|  David|     France|              1.0|(4,[1],[1.0])|

|  2| Martin|      Spain|              3.0|(4,[3],[1.0])|

|  3|  Jason|        USA|              0.0|(4,[0],[1.0])|

|  4|  Daiel|         UK|                4.0|(4,[],[])|

|  5|Moahmed| Bangladesh|     2.0|(4,[2],[1.0])|

|  6|  David|    Ireland|              1.0|(4,[1],[1.0])|

|  7|  Jason|Netherlands|          0.0|(4,[0],[1.0])|

+---+-------+-----------+-------------+-------------+




이제 결과 데이터 프레임에 피쳐 벡터가 포함된 새로운 컬럼이 추가된 것을 볼 수 있다.

Posted by '김용환'
,


스파크에서 제공하는 spark-shell을 이용한  Tokenizer 예이다.



 데이터 프레임을 생성한다.



scala>

val sentence = spark.createDataFrame(Seq(

(0, "Tokenization,is the process of enchanting words,from the raw text"),

(1, " If you want,to have more advance tokenization,RegexTokenizer,is a good option"),

(2, " Here,will provide a sample example on how to tockenize sentences"),

(3, "This way,you can find all matching occurrences"))).toDF("id","sentence")



이제 다음과 같이 Tokenizer API를 인스턴스화해서 토큰나이저를 생성한다.


import org.apache.spark.ml.feature.Tokenizer


val tokenizer = new Tokenizer().setInputCol("sentence").setOutputCol("words") 





이제 다음과 같이 UDF를 사용해 각 문장의 토큰 개수를 계산한다.




import org.apache.spark.sql.functions._


val countTokens = udf { (words: Seq[String]) => words.length } 




이제 각 문장에서 토큰을 얻는다.



val tokenized = tokenizer.transform(sentence) 







tokenized.show(false) 



+---+------------------------------------------------------------------------------+------------------------------------------------------------------------------------------+

|id |sentence                                                                      |words                                                                                     |

+---+------------------------------------------------------------------------------+------------------------------------------------------------------------------------------+

|0  |Tokenization,is the process of enchanting words,from the raw text             |[tokenization,is, the, process, of, enchanting, words,from, the, raw, text]               |

|1  | If you want,to have more advance tokenization,RegexTokenizer,is a good option|[, if, you, want,to, have, more, advance, tokenization,regextokenizer,is, a, good, option]|

|2  | Here,will provide a sample example on how to tockenize sentences             |[, here,will, provide, a, sample, example, on, how, to, tockenize, sentences]             |

|3  |This way,you can find all matching occurrences                                |[this, way,you, can, find, all, matching, occurrences]                                    |

+---+------------------------------------------------------------------------------+------------------------------------------------------------------------------------------+




마지막으로 각 토큰을 다음과 같이 각 원본 문장에 대해 표시한다. tokens 개수가 있다. 





scala> 

tokenized.select("sentence", "words").withColumn("tokens", countTokens(col("words"))).show(false)
+------------------------------------------------------------------------------+------------------------------------------------------------------------------------------+------+
|sentence                                                                      |words                                                                                     |tokens|
+------------------------------------------------------------------------------+------------------------------------------------------------------------------------------+------+
|Tokenization,is the process of enchanting words,from the raw text             |[tokenization,is, the, process, of, enchanting, words,from, the, raw, text]               |9     |
| If you want,to have more advance tokenization,RegexTokenizer,is a good option|[, if, you, want,to, have, more, advance, tokenization,regextokenizer,is, a, good, option]|11    |
| Here,will provide a sample example on how to tockenize sentences             |[, here,will, provide, a, sample, example, on, how, to, tockenize, sentences]             |11    |
|This way,you can find all matching occurrences                                |[this, way,you, can, find, all, matching, occurrences]                                    |7     |
+------------------------------------------------------------------------------+------------------------------------------------------------------------------------------+------+



원본 문장, 단어 모음, 토큰 수를 포함하는 토큰 데이터 프레임 정보를 출력한다.




그러나 RegexTokenizer API를 사용하면 더 좋은 결과를 얻을 수 있다. 다음과 같이 RegexTokenizer API를 인스턴스화하여 정규식 토큰나이저를 생성한다.




import org.apache.spark.ml.feature.RegexTokenizer



val regexTokenizer = new RegexTokenizer()

                    .setInputCol("sentence")

                    .setOutputCol("words")

                    .setPattern("\\W+")

                    .setGaps(true)




이제 다음처럼 각 문장에서 토큰을 얻는다.



val regexTokenized = regexTokenizer.transform(sentence)


regexTokenized.select("sentence", "words")

             .withColumn("tokens", countTokens(col("words")))

             .show(false)





+------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------+------+

|sentence                                                                      |words                                                                                      |tokens|

+------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------+------+

|Tokenization,is the process of enchanting words,from the raw text             |[tokenization, is, the, process, of, enchanting, words, from, the, raw, text]              |11    |

| If you want,to have more advance tokenization,RegexTokenizer,is a good option|[if, you, want, to, have, more, advance, tokenization, regextokenizer, is, a, good, option]|13    |

| Here,will provide a sample example on how to tockenize sentences             |[here, will, provide, a, sample, example, on, how, to, tockenize, sentences]               |11    |

|This way,you can find all matching occurrences                                |[this, way, you, can, find, all, matching, occurrences]                                    |8     |

+------------------------------------------------------------------------------+-------------------------------------------------------------------------------------------+------+






이전 코드 라인은 원본 문장, 단어 모음, 토큰 수를 포함하는 RegexTokenizer를 사용해 토큰화된 데이터 프레임 정보를 출력한다.  RegexTokenizer를 사용해 토큰화가 훨씬 좋아졌다.





이제 여기서 정지 (stop word)를 삭제해 보자.



스톱 워드(stop word)는 일반적으로 단어가 자주 나타나지만 많은 의미를 지니지 않아서 일반적으로 입력에서 제외되야 하는 단어이다. 


스파크의 StopWordsRemover는 입력으로 일련의 문자열을 받는다. 


일련의 문자열은 Tokenizer 또는 RegexTokenizer에 의해 토큰화된다. 


그리고 입력 문자열에서 모든 중지 단어를 제거한다. 중지 단어 목록은 stopWords 매개 변수에 의해 지정된다. 




StopWordsRemover API의 현재 구현은 덴마크어, 네덜란드어, 핀란드어, 프랑스어, 독일어, 헝가리어, 이탈리아어, 노르웨이어, 포르투갈어, 러시아어, 스페인어, 스웨덴어, 터키어, 영어에 대한 옵션을 제공한다. (한국어는 없다.)



예를 제공하기 위해 이전 Tokenizer 예를 이미 토큰화되어 있기 때문에 간단하게 확장할 수 있다. 그러나 이 예에서는 RegexTokenizer API를 사용한다.



먼저 StopWordsRemover API에서 다음과 같이 StopWordsRemover 인스턴스를 생성한다.


import org.apache.spark.ml.feature.StopWordsRemover


val remover = new StopWordsRemover()

            .setInputCol("words")

            .setOutputCol("filtered")




이제 다음과 같이 모든 중지 단어를 제거하고 결과를 출력하자.


scala>

val newDF = remover.transform(regexTokenized)


newDF.select("id", "filtered").show(false)


+---+-----------------------------------------------------------+

|id |filtered                                                   |

+---+-----------------------------------------------------------+

|0  |[tokenization, process, enchanting, words, raw, text]      |

|1  |[want, advance, tokenization, regextokenizer, good, option]|

|2  |[provide, sample, example, tockenize, sentences]           |

|3  |[way, find, matching, occurrences]                         |

+---+-----------------------------------------------------------+




이전 코드 라인은 필터링된 데이터 프레임에서 중지 단어를 제외하고 출력한다.








Posted by '김용환'
,



CountVectorizer와 CountVectorizerModel은 텍스트 문서를 토큰 개수의 벡터로 변환할 수 있다. 


CountVectorizer는 어휘 추출을 위한 에스터메이터(estimator)로 사용되어 CountVectorizerModel을 생성한다. 


해당 모델은 어휘를 통해 문서에 대한 희소한 표현을 생성한 다음 LDA와 같은 알고리즘으로 전달할 수 있다.


다음은 피쳐를 만드는 간단한 데이터 프레임을 생성하는 예이다.



scala>

val df = spark.createDataFrame(

Seq((0, Array("Jason", "David")),

(1, Array("David", "Martin")),

(2, Array("Martin", "Jason")),

(3, Array("Jason", "Daiel")),

(4, Array("Daiel", "Martin")),

(5, Array("Moahmed", "Jason")),

(6, Array("David", "David")),

(7, Array("Jason", "Martin")))).toDF("id", "name")



scala>

df.show(false)


// 결과


+---+----------------+

|id |name            |

+---+----------------+

|0  |[Jason, David]  |

|1  |[David, Martin] |

|2  |[Martin, Jason] |

|3  |[Jason, Daiel]  |

|4  |[Daiel, Martin] |

|5  |[Moahmed, Jason]|

|6  |[David, David]  |

|7  |[Jason, Martin] |

+---+----------------+





대부분의 경우 setInputCol을 사용해 입력 컬럼을 설정할 수 있다. 다음과 같이 코퍼스의 CountVectorizerModel 오브젝트를 피팅한다.



scala>


import org.apache.spark.ml.feature.CountVectorizer

import org.apache.spark.ml.feature.CountVectorizerModel


val cvModel: CountVectorizerModel = new CountVectorizer()

                          .setInputCol("name")

                          .setOutputCol("features")

                          .setVocabSize(3)

                          .setMinDF(2)

                          .fit(df)



이제 추출기를 사용해 다음과 같이 토큰 개수에 대한 벡터를 얻는다.



scala>

val feature = cvModel.transform(df)


scala> 


feature.show(false)

+---+----------------+-------------------+

|id |name            |features           |

+---+----------------+-------------------+

|0  |[Jason, David]  |(3,[0,1],[1.0,1.0])|

|1  |[David, Martin] |(3,[1,2],[1.0,1.0])|

|2  |[Martin, Jason] |(3,[0,2],[1.0,1.0])|

|3  |[Jason, Daiel]  |(3,[0],[1.0])      |

|4  |[Daiel, Martin] |(3,[2],[1.0])      |

|5  |[Moahmed, Jason]|(3,[0],[1.0])      |

|6  |[David, David]  |(3,[1],[2.0])      |

|7  |[Jason, Martin] |(3,[0,2],[1.0,1.0])|

+---+----------------+-------------------+





이름에 대한 피쳐가 생성되었다.

Posted by '김용환'
,



spark-shell에서 Failed to start database 'metastore_db' with class loader 에러가 발생하면..


Caused by: org.apache.derby.iapi.error.StandardException: Failed to start database 'metastore_db' with class loader org.apache.spark.sql.hive.client.IsolatedClientLoader$$anon$1@6d4a82, see the next exception for details.

  at org.apache.derby.iapi.error.StandardException.newException(Unknown Source)

  at org.apache.derby.impl.jdbc.SQLExceptionFactory.wrapArgsForTransportAcrossDRDA(Unknown Source)

  ... 153 more

Caused by: org.apache.derby.iapi.error.StandardException: Another instance of Derby may have already booted the database /usr/local/spark-2.1.0-bin-hadoop2.7/metastore_db.




다음 metastore lock 파일을 삭제하고 다시 spark-shell을 재기동한다.

$ rm metastore_db/dbex.lck




Posted by '김용환'
,


"네이버링"이라는 단어를 사용한다면, BGP 간을 연결하는 작업이라고 기억하는 것이 좋다.




https://www.networkcomputing.com/data-centers/bgp-basics-internal-and-external-bgp/1830126875

https://www.slideshare.net/vamsidharnaidu/bgp-1


Posted by '김용환'
,

[펌] Istio 내부 구조

Cloud 2018. 5. 24. 20:19




openshift에서 밀고 있는 istio의 개념을 살펴본다. (redhat 원래 발표자가 한 말은 아니지만 이해한 내용을 기반으로 거의 들어맞는 얘기를 해본다)




서비스에 다양한 기능(discovery, lb, resiliency, metrics, tracing)을 가진 jvm 애플리케이션에 docker로 개발하는 MSA를 진보적으로 바꿔보자. 





istio를 사용함으로서..

서비스만 jvm 애플리케이션으로 개발하고 나머지는 istio가 책임져 줄께.. 


kubenetes를 사용할꺼야. Pod 안에 jvm container 하나 있고, sidecar container(envoy)라는 것을 추가할 꺼야. 

sidecar container(envoy)를 사용하면 쉽게 MSA 서비스만 개발하면 될꺼야.





istio control plane이 쿼터 부터 api, config, rate limiting, ca 등등을 지원할꺼야. 


프로토콜? http는 물론 gRPC, TCP(TLS 포함)까지 지원한다.

근데 thrift는 지원하지 않아 보이지만.. 최근에 추가되었다.

https://github.com/istio/istio/tree/master/vendor/github.com/apache/thrift









실제 내부 구조는 다음과 같다. enovy와 Mixer가 핵심적인 서비스로 동작한다. 


https://developer.ibm.com/dwblog/2017/istio/







Posted by '김용환'
,

구글 클라우드에 apache kafka as service가 추가되었다. 



https://cloud.google.com/blog/big-data/2018/05/google-cloud-platform-and-confluent-partner-to-deliver-a-managed-apache-kafka-service




https://www.confluent.io/confluent-cloud/?utm_source=cloud.google.com&utm_medium=site-link&utm_campaign=gcp&utm_term=term&utm_content=content



Posted by '김용환'
,