심심해서 하는 블로그 :: '분류 전체보기' 카테고리의 글 목록 (2 Page)

Github > https://github.com/ssooni/data_mining_practice

- 자세한 소스코드는 Github에 올렸습니다.

- 데이터 파일도 올려 드리고 싶지만 실제 카카오톡 대화내용이라 일부분만 올렸습니다.

- 소스코드는 짬짬히 업로드할 예정입니다.




클러스터링

비지도학습의 대표 주자인 클러스터링은 각각의 노드들의 군집들을 산출해줍니다.

그 중에서 가장 간단한 게 K-means 알고리즘이고, 때마침 학교에서 공부한 적이 있으니까 해봐야겠다는 생각을 했습니다. 

하지만 이 알고리즘은 클러스터가 몇 개 나올것이다는 것을 제가 직접 설정해야 하는 점이 마음에 안들었습니다.

지금 Input으로 들어오는 벡터는 300차원이라 어디에 군집에 형성되어 있는지 짐작하기 어렵기 때문에 K-means 알고리즘은 사용하기 어렵다고 판단하였습니다.

저는 군집 갯수를 사전에 설정하지 않아도 군집을 산출해주는 알고리즘이 필요했고, 그 결과 DBSCAN 알고리즘을 선정하였습니다.


DBSCAN 

DBSCAN은 밀도 기반의 클러스터링 기법입니다.

DBSCAN의 클러스터는 아래 두 가지 조건에 의해 생성됩니다.


1. 이웃하는 점들의 간격이 사용자가 정한 거리(eps) 이내에 있는 경우를 산출

2. 군집에 포함하는 점들이 최소 클러스터 원소 수(min_samples)보다 많은 경우를 산출합니다.

min_sample = 3 인 경우


Core Point는 1번 조건과 2번 조건을 모두 충족하는 Point를 의미합니다. (빨간 점) 

Non-Core Point는 1번 조건을 만족하지만 2번 조건을 만족하지 못하는 Point입니다. (파란 점)

Non-Core Point는 Cluster를 이루는 경계 점 역할을 합니다.

Noise는 2개의 조건을 모두 만족하지 않은 경우로 클러스터를 구성하지 않습니다. (녹색 점)


이런 방법으로 클러스터를 찾는 것의 장점은 클러스터의 갯수를 지정할 필요 없다원형이 아닌 클러스터도 추출이 가능하다는 점입니다.

다른 알고리즘은 클러스터의 중심점을 이동하면서 클러스터 중심점 내부에 몇개의 점이 들어 있는 가에 집중되어 있기 때문에 클러스터의 모양이 필연적으로 원형으로 형성됩니다.

하지만 DBSCAN은 각각 개별 포인트 간의 거리를 측정하면서 Non-Core Point에 의해 클러스터의 경계가 형성되기 때문에 원형이 아닌  모양의 클러스터도 형성이 가능하다는 점이 장점입니다.


하지만 데이터에 대한 충분한 이해도를 가지고 있지 않는다면 eps와 min_points 값을 정하는 것이 어려운 것이 단점입니다.

다른 알고리즘은 단순히 10개의 분류를 만들어 달라 하면 10개를 만들어 주기 때문에 데이터 내부의 값들을 이해하지 않아도 충분히 클러스터를 만들어 낼 수 있습니다.

하지만 DBSCAN은 적어도 포인트 간이 이루는 최대 거리가 얼마인지 정도는 파악해야 유효한 클러스터를 만들어 낼 수 있습니다.


Word Vector Clustering

워드 백터를 클러스터링하면 어떤 결과가 나올까? 하는 의문에서 시작하였습니다.

모든 코드는 GitHub를 참조바랍니다.




1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from sklearn.cluster import DBSCAN
from gensim.models import Word2Vec
 
def cluster(eps, min_sample):
    # Word Vector를 Load 합니다.
    model = Word2Vec.load("./result/embedding.model")
 
    word_vector = model.wv.vectors
    match_index = model.wv.index2word
    model.init_sims(replace=True)
 
    # 두 글자이상 한글인 경우만 사용
    han = re.compile(r"[가-힣]{2,}")
 
    # DBSCAN 알고리즘 적용
    dbscan = DBSCAN(eps=eps, min_samples=min_sample)
    clusters = dbscan.fit_predict(word_vector)
 
    df = pd.DataFrame(clusters, columns=["cluster"], index=match_index).reset_index()
    df.columns = ["word""cluster"]
    print(df.head())
 
    # 한글만 필터링 처리
    df = df[df["word"].apply(lambda x: len(han.findall(x)) > 0)]
 
    # 노이즈 포인트 제거
    df = df[df["cluster"!= -1]
 
    print(df.groupby(["cluster"]).count())
 
    df.to_excel(pd.ExcelWriter("./result/cluster.xlsx"), index=False)
 
cs


line 15-16

본격적으로 DBSCAN 알고리즘을 적용하는 부분입니다.

eps와 min_samples를 정하여 넣어준 후 line 16에서 word vector를 클러스터링 해주었습니다.

fit_predict 함수를 사용하면 클러스터링된 결과를 리스트로 산출해줍니다.

이 때 -1은 노이즈 포인트를 의미합니다.


line 23-27

클러스터링 후 데이터를 일부 처리하였습니다.

2글자 이상의 한글만 유효한 결과로 선정하였으며, 노이즈 포인트도 제거 하였습니다.


이 프로그램을 실행하면 아주 허접한 그래프를 보여주었습니다.


클러스터링을 했을 때 종결 어미나 일자, 직책 같은 정보를 담고 있는 클러스터를 보고 신기했습니다.

뭔가 클러스터링을 하면 전체적인 주제를 알 수 있지 않을까? 생각 했지만 아쉽게도 그런 정보는 나타나질 않았네요.

찾아 보니까 LDA라는 걸 사용하면, 토픽 모델링이 가능하다고 하는데 다음번엔 그것을 시도해볼까합니다.

 

긴 글 읽어주셔서 감사합니다.

공감 버튼은 작성자에게 큰 동기부여가 됩니다.


,

연극의 줄거리보다 연극이 재밌는지 궁금한 분들을 위한 선 요약

1. 추리와 스릴러 장르다운 긴장감있는 사건 전개

2. 놀래키는 장면이 많지 않아서 좋았습니다.

3. 자리는 맨 앞 TV 있는 쪽 좌석 추천

4. 연인 친구들과 가볍게 즐기기 좋은 연극입니다.




감자님은 스릴러, 추리 장르를 좋아하고 저는 로맨스 코미디를 좋아하는 편입니다.

물론 좋아하는 장르는 그 연극표를 구했을 때 볼 수 있는 것이고, 또 감자님이 당첨이 되어서 스릴러 장르의 크리미널 시즌 4를 관람하게 되었습니다.


작은 규모의 연극 극장이지만 소품들이 제법 잘 구성되어 있습니다.

지정석이 아닌 자유석으로 추천드리는 자리는 앞 TV가 있는 쪽 좌석을 추천드립니다. 

배우들이 누워있거나 TV로 메세지를 전달하는 경우가 있는데, 반대쪽에서는 잘 보이지가 않아서 답답했습니다.

아 그리고 어떤 자리는 쓰레기통 역할을 할 수도 있습니다.


연극이 추리극의 성격을 가지고 있어서 전체적인 줄거리 소개는 간단하게 하고 넘어 가겠습니다.




전체 줄거리

어느 날 산장에 아무 이유 없이 4명이 감금됩니다. 

자신이 갇힌 이유도 누가 자신을 감금시켰는지도 알 수 없습니다.

서로 간의 공통점을 확인하려는 과정에서 4명 모두 하나의 사건에 연루되어 있다는 것을 알게 됩니다.

방 안에 나가기 위한 단서를 찾지만, 범인은 끊임없이 시간으로 압박을 가합니다.

과연 그들은 무사히 산장을 탈출하게 될까요?


감상평

추리하는 것을 좋아하고 어떤 게임에서 숨어있는 것을 찾는 것을 좋아한다면 충분히 재미있을 거라고 생각합니다.

우려했던 것과는 달리 놀래키는 장면이 적었습니다. 


4명의 배우분들이 처음부터 끝까지 흥분 상태로 극을 진행합니다. 

누군가가 감금했고 시간이 줄어드는 것을 보면서 마음을 진정할 용자는 흔치않겠지만 매순간순간 흥분을 해서 정신이 없긴 했습니다.


그리고 연극이 끝나면 배우분들과 포토타임도 준비되어 있습니다.

사진으로 추억을 간직하고자 하는 분들에게는 좋은 장점이라 생각합니다.

연인과 친구들과 가볍게 즐기기 괜찮은 연극이라 생각합니다.


긴 글 읽어 주셔서 감사합니다.

공감 버튼은 작성자에게 큰 동기부여가 됩니다. 

'눈누난냐 > 연극' 카테고리의 다른 글

[대학로 연극] 기묘여행 리뷰  (2) 2018.12.16
[대학로 연극] 러브 스코어 관람 후기  (6) 2018.09.09
,
연극의 줄거리보다 연극이 재밌는지 궁금한 분을 위한 선 요약
1. 배우분들의 훌륭한 연기력과 문학 소설 같은 시나리오
2. 극 전반적 분위기가 무겁습니다. 단 한 번도 웃음이 나오는 장면이 없었습니다.
3. 마음이 무거워지지만 저는 추천 드립니다.





감자님의 운은 어디까지인 걸까요..
연극표 당첨되서 또 문화생활을 이렇게 누릴수 있었습니다.

러브 스코어 이후 처음으로 관람하게 되는 연극이였습니다.
러브 스코어의 밝고 쾌할한 느낌과는 달리 한편의 묵직한 소설책을 읽은 듯한 느낌으로 연극을 관람했어요.

전체적인 줄거리 
기묘여행은 살인 사건의 피해자와 가해자 가족들이서 여행하는 연극 제목 그대로 기묘한 여행이다.

피해자 가족들의 상처와 아픔
가해자 가족들의 죄책감
그리고 죽은 영혼으로 등장하는 피해자
자신의 죄를 인정하고 그 댓가를 치룰려는 가해자

이들이 함께 있는 연극의 분위기는 당연히 무겁고 어둡습니다.

피해자 가족 입장은 당연히 가해자에 해한 복수.
사랑하는 딸의 죽음을 쉽게 용서할 수는 없습니다.
가해자를 만나 반드시 본인 손으로 죽이겠다는 다짐으로 여행을 떠납니다.

반대로 가해자 부모는 가해자의 생존이 목적입니다.
가해자 본인은 항소를 포기하며, 자신의 죄에 대한 벌을 받고자 합니다.
자기 자식이 살인자라 하여도 부모들에게는 하나 뿐인 자식이기에 인면수심임에 불구하고 피해자 가족에게 그를 설득해달라고 부탁합니다.

과연 그들은 각자의 목표를 이룰 수 있을까요..?


고통스러운 사람끼리 위로가 과연 통할 것인가?
모든 등장 인물이 각각의 고통을 가지고 있습니다.
피해자 가족, 가해자 가족, 피해자, 가해자, 여행을 주선한 사람까지 모든 이들이 고통스러운 나날을 보냈던 사람들입니다.

인형을 각각 가해자와 피해자의 이름을 붙이고 피해자 가족은 가해자에게 칼을 꽂고, 가해자 가족은 피해자를 안아주는 장면이 있습니다.
각자의 입장이 되어보며 각자의 고통을 이해하는 시간이 되었겠죠.

그런데 오히려 감정의 교류는 피해자 어머니가 찌르기를 거부하고, 가해자 어머니가 대신 찌를 때 감정이 교류됩니다.

단 한 차례만 허벅지를 찌르는 피해자 아버지
수차례를 가슴을 찌르는 가해자 어머니
 
그동안 감정을 숨겨오던 가해자의 어머니의 행동으로 가해자의 살인은 가해자 부모에게도 큰 상처와 분노로 남은 것을 행동으로 보여주는 장면이 아닐까 저는 생각합니다.
그렇다고 서로의 감정을 확인한 이 장면이 두 가족간의 화해의 장면은 결코 아닙니다.
연극이 끝나는 시점까지 피해자 가족은 가해자에게 단 한번도 설득한 적이 없기 때문입니다.
서로의 고통을 확인하더라도 자신이 받는 고통이 더욱 크기에 서로 위로를 받거나 이해를 하지 않습니다.

연극을 보는 내내 마음 한구석이 너무 무거웠습니다.

해피엔딩도 아닌, 새드 엔딩도 아닌 살아 남은자는 계속 아픔을 가지고 살아 갈 것이다라는게 결말이였기 때문이죠.

많은 관객들이 눈물을 많이 흘렸습니다. 피해자 가족 입장에서 죽이고 싶지만 죽일 수 없는 감정에 많은 공감을 했던 것 같습니다.


그 누구도 행복하지 못한 결말, 그 누구도 원하는 것을 얻지 못한 결말에 답답함을 느낄지도 모르겠습니다. 

하지만 배우들의 탄탄한 연기력과 소설을 읽은 듯한 감동을 선사해주는 이 연극을 추천 드립니다.


긴 글 읽어주셔서 감사합니다.

공감 버튼은 글쓴이에게 큰 동기부여가 됩니다.

,


Github > https://github.com/ssooni/data_mining_practice

- 자세한 소스코드는 Github에 올렸습니다.

- 데이터 파일도 올려 드리고 싶지만 실제 카카오톡 대화내용이라 일부분만 올렸습니다.

- 소스코드는 짬짬히 업로드할 예정입니다.




Word to Vector

Word2Vec은 간단하게 텍스트를 숫자로 변환하는 것을 의미합니다.

컴퓨터가 모든 텍스트의 의미를 이해해서, Context를 잘 잡아낸다면 굳이 이런 작업을 하지 않아도 됩니다.

하지만 아직까지 컴퓨터는 숫자로 이루어진 세계에 대한 연산을 더 잘 합니다. 

숫자로 이루어진 세계에서 마치 컴퓨터가 우리가 글을 이해하는 듯한 알고리즘을 적용한다면 문맥을 이해할 수 있지 않을까? 하는 취지에서 시작되었습니다.


그렇다면 텍스트를 특정한 벡터 좌표로 표현할려면 어떤 방법들이 있을까요.

하나는 각각의 차원에 텍스트가 가질 수 있는 특성을 부여하는 방법입니다.



각각의 차원에 특성을 두고 사람과 닭이 가지는 특성에 맞게 배치를 하였습니다. 

사람과 닭은 예제에서 보여준 4가지의 속성에 따라 서로 구분이 가능하다는 것을 알 수 있습니다.

하지만 원숭이와 사람을 이 속성이 따라 구분하면 어떻게 될까요?



사람과 원숭이는 4가지의 속성으로는 서로 구분이 가기 힘듭니다.

따라서 우리는 사람과 원숭이를 구분할 수 있는 특성을 추가적으로 넣어야 합니다.

이렇게 계속 다른 것과 구분을 하기 위해 특성을 추가하다보면 이 세상 모든 단어를 표현하는데 필요한 차원은 무한대에 가까울 것입니다.

그리고 그런 특성을 사람이 일일히 부여하는 것은 매우 피곤한 일입니다.


CBOW 방법 vs Skip-Gram 방법

그래서 이번에는 다른 방법을 사용하여, 텍스트를 벡터로 표현하고자 합니다.

 


위의 문제가 주어졌을 때 운동이나 밥, 노래들을 잘하는지는 이전 또는 이후 문장을 확인해야 알 수 있습니다. 

주변의 단어를 통해 중심 단어를 추측하는 방법이 CBOW 방법입니다.  



반대로 Skip-Gram 방법은 어떤 단어가 등장했을 때 근접한 단어가 발생하는 양상을 측정하는 방법입니다.

노래라는 중심 단어를 기준으로 앞뒤 N개의 단어가 등장하는 확률을 구하여 Vector로 만들어 줍니다.



관련 키워드 찾기

Word2Vec 모듈의 설치방법은 아래와 같습니다.

>> pip install word2vec


앞선 시리즈에서 형태소 분석기를 통해 Token을 만들었는데 그것을 이용해서 Word2Vec를 알고리즘을 적용합니다.

자세한 소스코드는 GitHub를 참조해주세요.





word2vector.py


1
2
3
4
5
6
7
8
9
10
11
12
13
14
def create_model(filename, skip_gram=False):
    tokens = pd.read_csv(filename)
    tokens = tokens[tokens["contents"].apply(lambda x: 'http' not in x)]
 
    sentence = tokens["token"].apply(lambda x: ast.literal_eval(x)).tolist()
    
    if skip_gram:
        model = Word2Vec(sentence, min_count=10, iter=20, size=300, sg=1)
    else:
        model = Word2Vec(sentence, min_count=10, iter=20, size=300, sg=0)
 
    model.init_sims(replace=True)
    model.save("./result/embedding.model")
 
cs


line 3

카카오톡의 대화 내용 중에서 링크를 첨부하는 경우를 제외하였습니다. 


line 5

Word2Vec의 Sentence는 iterable한 자료형이 들어가면 됩니다.

저는 이전 시리즈에서 형태소 분석기로 Tokenize한 결과를 넣었습니다.


line 7-10

Skip Gram 방법을 사용할지 CBOW 방법을 사용할지 선택합니다.

각각의 파라미터는 반드시 똑같이 따라 하실 필요는 없습니다.


size : 몇 차원의 Vector를 만들지 여부를 결정합니다.

min_count : 특정 빈도수 이상 발생한 단어에 대하여 Embedding을 합니다.

iter: 반복 횟수


line 13

완성된 모델을 파일로 저장합니다.


word2vector.py

1
2
3
4
5
def most_similar():
    model = Word2Vec.load("./result/embedding.model")
    print("용돈과 관련된 키워드 : ", model.most_similar("용돈"))
    print("졍이와 관련된 키워드 : ", model.most_similar("졍이"))
    print("쭈니와 관련된 키워드 : ", model.most_similar("쭈니"))
cs


line 2

앞서 생성한 모델을 읽어드립니다.


line 3 - 5

most_similar 함수는 Word Vector에 Cosine 값을 내적을 통해 구한 뒤 그 값이 클수록 유사하다는 Cosine Similarity 방법을 사용하여 특정 단어와 관련된 단어를 찾아줍니다. 



관련도 수치가 낮은 게 아쉽긴 합니다만, 관련된 단어라고 생각하는 것들이 나와서 나름 만족 스럽습니다.

다음 시리즈에서는 벡터화된 데이터를 이용하여, 클러스터링을 하면 어떤 결과가 나오는지 확인해보도록 하겠습니다.


긴 글 읽어 주셔서 감사합니다.

공감 버튼은 글쓴이에게 큰 동기부여가 됩니다.

 

,

영화 내용은 안 궁금하고 재밌는지 궁금한 사람들을 위한 선 요약

1. 도어락을 따고 들어가서 도어락인거지 자물쇠 땄으면 자물쇠가 영화 제목이였음

2. 도어락 어떻게 딴건지 추리는 없다.  모든 것은 우연이다.

3. 긴장은 긴장대로 하게하고 해소를 해주지 않는 영화 


범인에 대한 스포일러가 있으므로 보고 싶지 않다면 뒤로 넘어 가시는것을 추천 드립니다.

영화 자체를 추천하지는 않아서 봐도 상관은 없습니다만..




영화의 스토리를 요약하면 도어락으로 문을 잠궜으나 도어락을 뚫고 들어와서 집 안에 몰래 같이 사는 변태적인 범인이 있다는 내용의 영화인데 사실 도어락이랑 전혀 상관이 없다.


만약 자물쇠로 문을 잠궜으나 자물쇠를 뚫고 들어왔다면 이 영화 제목은 자물쇠가 되었을 것이다.


다만 소재가 현실에도 있을 수 있는 일을 기반으로 하였다는 것에는 좋은 시도라고 생각한다만..


모든 서사는 우연이다. 필연이 없다.

그 사람이 어떤 사람인지 어떻게 들어왔는지 그런거는 관객이 전혀 알 수 없다. 범인의 직업이 경비원인데 경비원이라고 우리집 비밀번호 알고 있으면 다 잘라야 한다. 

그리고 CCTV도 없는데 어떻게 비밀번호를 아는 건지 그냥 모든게 우연이다.

그렇게 문을 쾅쾅치면서 사람을 잡아 대는데 옆집 앞집은 우연히 비어있나봐 이웃한테 먼저 물어보고 그런것도 없네..


나름 필연을 맞추기 위해서 추리씬이 들어가긴 하는데, 

우연히 3200원 사용하는 사람을 발견하였고, 

두 사람이 서로 다른 길로 찾았는데 우연히 실종자의 범행 현장에 주인공이 도착했고 위기의 순간에 

갑자기 동료가 우연히 지나가다가 몸통박치기로 도와준다.


유력한 용의자가 있지만 범행은 계속 일어나는 걸 계속 보여주면서 관객들에게 "얘는 범인 아니야^^" 농락하는데 그 과정이 매우 짜증이 난다. 


주변 사람들이 다치거나 죽으니 긴장감을 주는데 전혀 해소를 해주지 않는다. 떡밥을 던져서 조금씩 해소해줄 만한데 3200원과 오래된 시계말고는 결정적인 떡밥을 던지지 않으니, 주인공이 직접 잡히는 수 밖에 범인을 공개할 수단이 없다.


그렇게 형사도 너무 허무하게 죽고 전지전능한 범인이 우연히 튀어나온 못에 한방 맞고 사망하니까 허무하다. 


이런 긴장감을 주는게 목적이라면 아주 성공했지만, 중간 중간에 긴장을 해소하고 관객들에게 숨도 좀 돌릴수 있는 시간을 줬으면 더 좋았을텐데하는 아쉬움이 있다.


마지막까지 침대 밑을 비추면서 긴장감을 줄 필요가 있을까 하는거다. 영화 보고 나와서 찝찝함을 잊을 수 없던 영화였다.

'눈누난냐 > 영화' 카테고리의 다른 글

[영화] 보헤미안 랩소디 후기  (6) 2018.12.11
암수살인을 보고 왔습니다 (스포주의)  (2) 2018.10.26
,


영화 내용은 안 궁금하고 재밌는지 궁금한 사람들을 위한 선 요약

1. 마지막 20분을 위한 영화

2. 라라랜드가 재밌었다면, 이 영화도 재밌을 것 입니다.

3. 어? 나도 모르게 Queen 노래를 Playlist에 담고 있네? 



Queen 음악을 좋아하고, 즐겨듣던 사람으로써 퀸 음악을 영화관에서 듣게 된다는 것 만으로 행복했던 영화였다.

이 영화의 파급력이 대단하다고 느끼는 것은 동네 편의점에서 Queen 노래 그것도 Radio gaga를 듣을 수 있다는 것이다.


영화 보헤미안 랩소디는 퀸의 탄생부터 퀸의 전설적인 공연으로 기록되는 Live AID 까지 10년 가까히의 시간들을 영화에 담고자 했다.

영화의 앞부분은 퀸의 성공적인 모습을 압축하여 보여 주고자 하다 보니, 서사가 단순하다.


두 사람이 감정적으로 싸우다가 다른 멤버가 이거 해보자 하면, 오 좋은데? 하고 획기적인 노래를 만드는 것이 두여차례 반복한다.


그런데 이런 서사가 노래가 좋으니까 먹힌다.


We will Rock You와 보헤미안 랩소디, Another one bites the dust 가 보통 좋은 노래가 아니니까..


그 사이에 프레디 머큐리의 정체성을 찾아가는 과정을 담고 있다.

자신이 동성을 좋아한다는 것을 인정하면서 고독과 고뇌를 담고자 했다. 

그 시기부터 프레디가 퀸과 멀어지고 음악을 뒤로 하고 술과 약으로 사는 방탕하는 삶을 살게되면서 그를 죽게 만든 에이즈에 걸린다.


(물론 실제로 프레디 본인이 에이즈인 것을 알게 된 시기는 Live AID 이후의 일이지만, 영화의 전개를 위해 필요한 픽션이라고 생각한다.)


동성애 구간을 아주 어둡게 표현하고, 이성애 구간을 밝고 낭만적으로 표현한거에 불만을 가질 수도 있지만, 당대 시대상을 반영한 것이라 생각한다.


다시 가족과 Queen으로 돌아와 Live AID 공연으로 하는 것으로 영화는 마무리된다.



Live AID 공연을 보여준 20분은 환상적이였다.

유튜브로 보는 공연에서 카메라 앵글이 다소 아쉬울 때가 많았는데, 다양한 앵글로 공연을 볼 수 있어서 좋았다.

그리고 저 공연을 직접 경험하지 못한 세대로써 생생한 관객들의 떼창은 마치 현장에서 그의 공연을 보는 듯한 경험을 제공해주었다.


이렇게 영화는 끝이 난다.


퀸의 다큐멘터리를 봤을 때 마음 아프고 프레디 머큐리가 대단하다고 느낀 부분은 Live AID의 모습이 아닌 에이즈에 걸려 힘들었음에도 불구하고, 죽는 마지막 순간까지 음악을 사랑했다는 것이였다.


퀸을 처음 접하는 사람들에게 알려주고 싶은 아쉬움은 있있다. 프레디가 이렇게 열정적이였다고!!! 

하지만 마음의 아픔이 되었던 날이였고, 좋은 기억들만 가지고 퀸을 좋아해주길 감독이 바라는 것 아닐까 생각한다.


2시간 20분이 전혀 길지 않게 느껴지는 영화였다.

언젠가 왓챠플레이나 넷플릭스에 보헤미안 랩소디가 올라오는 날이 온다면 마지막 20분은 꼭 다시 경험해보고 싶다.

'눈누난냐 > 영화' 카테고리의 다른 글

[영화] 도어락 리뷰  (3) 2018.12.11
암수살인을 보고 왔습니다 (스포주의)  (2) 2018.10.26
,

rGithub > https://github.com/ssooni/data_mining_practice

- 자세한 소스코드는 Github에 올렸습니다.

- 데이터 파일도 올려 드리고 싶지만 실제 카카오톡 대화내용이라 일부분만 올렸습니다.

- 소스코드는 짬짬히 업로드할 예정입니다.




카카오톡 시리즈의 2번째로 워드클라우드를 만들어 보았습니다.

이전 시간에서 만들었던 명사 추출 결과를 사용하여 워드클라우드를 그릴 것입니다.

 

1. 단어의 빈도 수를 측정  

워드클라우드에서 글자 크기를 정하는 기준을 저는 단어의 빈도수로 결정하였습니다. 

특정 컬럼을 그룹으로 하여 집계하는 방법은 pandas에서 간단하게 구현이 가능합니다.


mwordcloud.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def draw_wordcloud(kkma_result):
    # List로 되어있는 열을 Row 단위로 분리
    tokens = pd.DataFrame(kkma_result["token"].apply(lambda x: ast.literal_eval(x)).tolist())
 
    tokens["Date"= kkma_result["Date"]
    tokens["Speaker"= kkma_result["Speaker"]
    tokens["timetype"= kkma_result["timetype"]
    tokens["time"= kkma_result["time"]
    tokens["contents"= kkma_result["contents"]
    
    tokens = tokens.set_index(["Date""Speaker""timetype""time""contents"])
    tokens = tokens.T.unstack().dropna().reset_index()
 
    tokens.columns = ["Date""Person""time_type""time""sntc""index""token"]
    print(tokens.head())
 
    # 빈도수 집계
    summary = tokens.groupby(["token"])["index"].count().reset_index()
    summary = summary.sort_values(["index"], ascending=[False]).reset_index(drop=True)
 
    """ 이하 생략 """ 
cs


모든 소스코드는 GitHub를 참조바랍니다.


제가 정의한 draw_wordcloud()는 집계를 하고 빈도수가 많은 순서대로 정렬한 후 워드클라우드를 생성합니다.


line 2-15

시리즈 1의 결과를 보면 token 컬럼 안에 리스트로 토큰들을 저장하도록 구성되어 있습니다. 


이렇게 구성되어 있으면 분석하기 상당히 불편하므로 리스트 안에 있는 원소를 풀어서 새로운 행을 구성하는 것이 line2 - 14 입니다.

만약 시리즈를 이어서 하는 것이 아니라면 line 2-14번은 생략하셔도 됩니다.


line 18 - 19

pandas.DataFrame의 groupby는 특정한 컬럼을 그룹으로 묶어줍니다.

그리고 그룹을 대상으로 min(), max(), count()등의 집계함수를 제공합니다. 

이 경우에는 모든 구간에 대하여 token을 기준으로 발생한 빈도수를 집계하였습니다. 

순위를 측정하기 위해서 집계한 결과를 내림차순으로 정렬까지 완료합니다.


2. 워드클라우드  

데이터과학에서 시각화는 생각보다 큰 비중을 차지하는 분야입니다. 

어떻게 보여줘야 분석에 용이하고 데이터 분석결과를 받아 드리는 사람들이 쉽게 받아 드릴수 있기 때문입니다.

측정한 빈도수를 기반으로 Line나 막대 그래프도 그릴 수 있습니다만, 몇 번 나왔는지에 큰 의미를 두지 않는다면 빈도수는 TMI가 될 수 있습니다.

키워드에만 집중하기 위한 시각화 표현 기법으로 주로 워드클라우드를 사용합니다.

그리고 Python의 WordCloud 모듈은 개발자들에게 쉽고 빠른 방법으로 워드클라우드를 생성하는 것을 도와줍니다.


>> pip install wordcloud


워드클라우드 모듈을 설치가 완료되었다면 이제 사용하는 소스 코드를 작성해 봅시다.




mwordcloud.py

1
2
3
4
5
6
7
8
def draw_wordcloud(kkma_result):
 
    """ 상단 생략 """
 
    wc = WordCloud(font_path='./font/NanumBrush.ttf', background_color='white', width=800, height=600).generate(" ".join(summary["token"]))
    plt.imshow(wc)
    plt.axis("off")
    plt.show()
cs


line 4

WordCloud의 generate() 함수는 공백으로 분리된 문자열 리스트를 받습니다.

그리고 가장 첫 번째의 단어를 가장 크게, 가장 마지막 단어를 가장 작게 표현합니다.

앞서 빈도 수를 기준으로 token을 정렬하였기 때문에 가장 많은 빈도 수가 나오는 문자가 가장 크게 나올 것입니다. 

아래 그림처럼 말이죠.


 

카카오톡은 이모티콘과 사진, 동영상을 전송한 채팅 기록에 대하여는 (이모티콘) 이런 식으로 저장하니까 이모티콘과 사진이 압도적으로 많이 나오는 걸 볼 수 있습니다.


3. 특정 단어를 제외하자  

정말 직업으로 이모티콘을 개발하는 사람끼리 얘기하는 것이라면 이모티콘 단어가 큰 의미로 받아 지겠지만, 그냥 개발자와 그 여자친구간에 얘기에서 이모티콘은 큰 의미가 있는 단어는 아닙니다.

이번 단계에서는 노이즈라고 생각하는 단어들을 제외하는 과정을 진행해서 유효한 단어들만 보여주고 싶습니다.


mwordcloud.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def get_except_keyword(filename):
    keyword_list = list()
    with open(filename, encoding='utf-8') as f:
        for keyword in f.readlines():
            keyword_list.append(keyword.strip())
    print(keyword_list)
    return keyword_list
 
def draw_wordcloud(kkma_result):
 
    """ 상단 생략 """
    
    # 특정 단어 필터링
    except_keyword = get_except_keyword("./raw_data/except_word.txt")
    summary = summary[summary["token"].apply(lambda x: x not in except_keyword)]
    summary = summary[summary["token"].apply(lambda x: len(x) > 1)]
 
    wc = WordCloud(font_path='./font/NanumBrush.ttf', background_color='white', width=800, height=600).generate(" ".join(summary["token"]))
    plt.imshow(wc)
    plt.axis("off")
    plt.show()
 
cs



./raw_data/except_word.txt 에는 아래와 같이 제외하고자 하는 단어들의 리스트가 저장 되어있습니다.


./raw_data/except_word.txt


line 14 - 16

get_except_word() 함수에서 해당 파일을 읽어서 리스트 형태로 제외 문자열을 가지고 있습니다.

line 15에서 해당 문자열 리스트 안에 token이 포함되어 있으면 제외하는 로직이 적용됩니다.

그리고 추가적으로 line 16에서 token의 글자 수가 1개인 경우도 제외하였습니다.



아까보다는 저한테는 필요한 것이 나오긴 했지만, 좀 더 필터링을 한다면 더 좋을 거 같아요.

근데 사각형 너무 식상하지 않나요?


4. 워드클라우드 Mask 적용

Mask를 적용하면 워드클라우드가 이미지에 따라서 맞춰서 단어를 배열해 줍니다. 

바탕색이 투명한 PNG 파일을 하나 준비합니다.

저는 여자친구가 좋아하는 데덴네 이미지를 하나 준비하였습니다.


mwordcloud.py

1
2
3
4
5
6
7
8
9
10
11
12
13
from PIL import Image
 
def draw_wordcloud(kkma_result):
 
    """ 상단 생략 """
    
    denne_mask = np.array(Image.open("./font/denne.png"))
 
    wc = WordCloud(font_path='./font/NanumBrush.ttf', background_color='white', width=800, height=600, mask=denne_mask).generate(" ".join(summary["token"]))
    plt.imshow(wc)
    plt.axis("off")
    plt.show()
 
cs

 

line 7

마스크로 사용할 이미지를 읽어서 Numpy.Array로 저장합니다. 


line 9

WordCloud 생성자에 mask 파라미터에 line 7에 만들어 둔 마스크를 넣어 줍니다.

 


실행한 결과입니다. 

원하는 이미지를 사용해서 적용해 보는 것이 좋을 거 같아요.

다음 시리즈는 Word2Vec을 적용해본것을 올려 볼까 합니다.


전체적인 소스코드는 GitHub를 참조하시고, 궁금한 사항이 있으시면 댓글 달아 주시면 최대한 빨리 답변드리겠습니다.


긴 글 읽어주셔서 감사합니다.

공감버튼은 작성자에게 큰 동기부여가 됩니다.

,

- 자세한 소스코드는 Github에 올렸습니다.

- 데이터 파일도 올려 드리고 싶지만 실제 카카오톡 대화내용이라 일부분만 올렸습니다.

- 소스코드는 짬짬히 업로드할 예정입니다.


오래 전부터 시도를 해보고 싶었지만, 카카오톡 대화 데이터를 이용해서 워드 클라우드부터 Word2Vector 등등 텍스트 마이닝에서 사용하는 기법을 사용해 볼려고 합니다.

통계적 지식은 거의 전무하지만, 1년간 우연히 텍스트 마이닝 프로젝트를 하면서 어깨 너머 보고 배운 것을 기반으로 소소한 프로젝트를 만들어 보았습니다.


프로젝트 폴더 구조


1. 카카오톡 대화를 Text 파일로 저장합니다.

컴퓨터 카카오톡을 켜서 아무 대화 방에서 Ctrl + S를 누르면 대화를 텍스트 파일로 저장이 가능합니다.

저는 여자친구와 나눈 대화를 실험 재료로 사용하고자 합니다.



그리고 프로젝트의 raw_data 폴더에 넣어 둡니다.

저는 raw_data/kko.txt 파일로 저장해두었습니다.



2. 정규표현식

정규표현식은 찾고자하는 텍스트가 일정한 패턴을 가지고 있을 때, 해당 패턴을 표현하기 위해 사용하는 언어입니다.

가독성이 썩 좋지 않아서 처음에 진입하기 꺼려지지만, 알아두면 텍스트에서 원하는 부분만 가져오기 편합니다.

카카오톡에 저장된 내용을 보면 다음과 같은 패턴을 알 수 있습니다.



언젠가 사용하리라 생각하면서, 일자 정보누가 몇 시어떤 말을하는지까지 패턴을 이용하여 추출하고자 합니다.

위의 패턴을 정규표현식으로 표현하면 다음과 같습니다.



꼼꼼하게 날짜의 패턴에서 숫자의 갯수를 지정하거나, 시간 부분도 표현이 가능하지만, 정형화된 패턴이라 너프하게 표현했습니다.  

이제 Python으로 정규표현식을 적용해봅시다.


regex.py의 일부분

1
2
3
4
5
6
7
8
9
def apply_kko_regex(msg_list):
    kko_pattern = re.compile("\[([\S\s]+)\] \[(오전|오후) ([0-9:\s]+)\] ([^\n]+)")
    kko_date_pattern = re.compile("--------------- ([0-9]+년 [0-9]+월 [0-9]+일) ")
 
    emoji_pattern = re.compile("["u"\U0001F600-\U0001F64F"  # emoticons
                               u"\U0001F300-\U0001F5FF"  # symbols & pictographs
                               u"\U0001F680-\U0001F6FF"  # transport & map symbols
                               u"\U0001F1E0-\U0001F1FF"  # flags (iOS)
                               "]+", flags=re.UNICODE)
cs


파이썬의 re package으로 정규표현식을 컴파일하여 문자열에서 패턴 추출을 할 수 있도록 제공합니다.

카카오톡 대화 부분과 날짜 패턴을 생성합니다. 그리고 추가적으로 이모지 패턴을 넣어줍니다. 

(이모지 패턴은 StackOverflow를 참고하여 작성하였습니다.)

이모지 패턴은 이후에 사용할 형태소 분석기 꼬꼬마(Kkma)에서 이모지를 만나면 에러가 발생해서, 이모지를 제거하기 위한 목적으로 만들었습니다.

regex.py의 일부분

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def apply_kko_regex(msg_list):    
 
    """ 상단 생략 """
 
    kko_parse_result = list()
    cur_date = ""
 
    for msg in msg_list:
        # 날짜 부분인 경우
        if len(kko_date_pattern.findall(msg)) > 0:
            cur_date = dt.datetime.strptime(kko_date_pattern.findall(msg)[0], "%Y년 %m월 %d일")
            cur_date = cur_date.strftime("%Y-%m-%d")
        else:
            kko_pattern_result = kko_pattern.findall(msg)
            if len(kko_pattern_result) > 0:
                tokens = list(kko_pattern_result[0])
                # 이모지 데이터 삭제
                tokens[-1= re.sub(emoji_pattern, "", tokens[-1])
                tokens.insert(0, cur_date)
                kko_parse_result.append(tokens)
 
    kko_parse_result = pd.DataFrame(kko_parse_result, columns=["Date""Speaker""timetype""time""contents"])
    kko_parse_result.to_csv("./result/kko_regex.csv", index=False)
 
    return kko_parse_result
cs

이후의 코드는 파일에서 읽은 메세지를 정규식을 이용하여 패턴을 추출하고 이모지의 경우 삭제하는 코드입니다.


line 14

문자열 Type str의 내장 함수 findall()은 정규식 패턴에 해당하는 모든 문자열을 추출해줍니다. 


line 18

특정 패턴을 가진 문자열을 치환하는 경우 re.sub()를 사용합니다.

re.sub(패턴, 치환 문자열, 대상 str) 

지금의 경우에는 모든 이모지 패턴의 문자를 메세지 텍스트에서 삭제하기 위해 사용하였습니다.


line 22-23

추출된 결과를 pandas.DataFrame에 저장하고 csv 파일로 저장합니다.





3. 한글 형태소 분석기 적용

우리가 글을 배울 때, 단어를 익히고 문장을 배우고 문장들을 이어서 문맥을 완성합니다. 

이처럼 텍스트를 분석할 때 처음부터 문장 전체를 사용하지 않습니다. 

작은 단위로 쪼개는 과정을 Tokenizing 라고 하는데, 형태소 단위까지 쪼개는 경우도 있고, 단어 중심으로 쪼개는 경우도 있습니다.


저는 한글 형태소 분석 모듈 KoNLPy를 이용하여 형태소 단위로 쪼개는 것을 선택하였습니다.

설치는 Anaconda Prompt에서 아래의 명령어를 입력하면 설치가 가능합니다.


>> pip install konlpy


KoNLPy에는 Hannanum, Kkma, Komoran 등의 형태소 분석 패키지를 제공하는데 저는 Kkma(꼬꼬마)를 사용했습니다.

특별한 이유는 없습니다.. 꼬꼬마라는 네이밍이 귀여워서 선택했을 뿐..


kkma_token.py의 일부분

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import re
import pandas as pd
from konlpy.tag import Kkma
 
 
def get_noun(msg_txt):
    kkma = Kkma()
    nouns = list()
    # ㅋㅋ, ㅠㅠ, ㅎㅎ  
    pattern = re.compile("[ㄱ-ㅎㅏ-ㅣ]+")
    msg_txt = re.sub(pattern, "", msg_txt).strip()
 
    if len(msg_txt) > 0:
        pos = kkma.pos(msg_txt)
        for keyword, type in pos:
            # 고유명사 또는 보통명사
            if type == "NNG" or type == "NNP":
                nouns.append(keyword)
        print(msg_txt, "->", nouns)
 
    return nouns
cs


line 10 - 11
카카오톡 데이터로 분석하다보니 ㅋㅋ ㅎㅎ 등을 제거하는 로직을 추가하였습니다.
ㅋㅋ, ㅎㅎ, ㅜㅜ는 그래도 꼬꼬마에서 무난하게 이모티콘으로 해석해 줍니다.
하지만 "ㅁㄹ데ㅂ제더레ㅐ벚대ㅏ에"와 고양이가 타이핑한 게 들어가니까 성능이 매우 느려져서 제거하는 것을 추가 했습니다. 

line 14
물론 kkma.nouns() 라는 명사만 따로 추출하는 함수를 제공해주는데, 모든 조합 가능한 명사를 제공하는 건지 마음에 안들어서 kkma.pos()를 사용하여 문장에 속하는 것들의 품사를 분석한 뒤 그 중에 명사나 고유 명사에 해당하는 것만 추출했습니다.

결과물


카카오톡 특징상 맞춤법, 띄어쓰기가 전혀 안되있다보니 정확성은 떨어집니다.

서로간의 애칭이나 신조어가 들어가는 경우에 더욱 이상하게 분리를 하는데 이 경우에는 사전에 추가를 해주면 됩니다.



4. Kkma 사전에 키워드 추가

Anaconda 설치경로/Lib/site-packages/konlpy/java 경로로 이동합니다.

kkma-2.0.jar의 압축을 풀어 준 후 폴더로 이동합니다.

폴더 안에 dic 폴더에 사전들이 있습니다.


해당 사전에 마지막에 추가하고자 하는 내용을 넣어 줍니다.

명사만 저는 추가하였습니다.




이전 폴더로 이동해서 jar 파일로 다시 아카이브한 후 덮어쓰면 사전이 적용 됩니다.


카카오톡 대화 내용에 형태소 분석기를 적용하여 보았습니다.

다음 포스팅에서는 워드클라우드를 생성하는 것을 주제로 찾아 뵙겠습니다.


도움되셨다면 공감버튼 눌러주세요.

공감 버튼은 작성자에게 큰 동기부여가 됩니다.



,



영화 내용은 안 궁금하고 재밌는지 궁금한 사람들은 위해 선 요약

1. 주지훈 연기 미쳤음 때리고 싶음. 김윤석은 말할 필요도 없고..

2. 심리싸움과 밀당에 2시간 뚝딱 

3. 무튼 재미있음 


감자님과 일주일에 한 번만나는데 2시간을 대화 한 마디 못하는 영화관에서 보낸다는건 조금 아깝다고 생각해서 둘이서 영화를 자주 보지는 않는다.


하지만 이번에 영화표가 생겼고 무슨 영화를 같이볼까 하다가 암수살인 이라는 영화를 봤다.

영화 개봉 전부터 유가족 분들과 문제로 이슈가 되었던 그 영화

그래서 처음에는 거부감도 있었지만 마땅히 끌리는 영화도 없었기 떄문에 이 영화를 보게되었다.


범죄수사물은 보통 범인은 끝까지 자기가 무죄라고 우기는 과정에서 힘들게 증거를 찾아서 범인의 죄를 입증하는 시나리오가 대부분이다.

그렇기 때문에 영화가 극적으로 보이기 위해서 찾아가는 과정을 어렵고 힘들게 전개한다.


근데 이 영화는 양상이 다르다.

이미 살인죄로 잡혀있는 범인이 진짜 증거는 다른 곳에 있고 사람을 많이 죽였다고 주장한다.

이 부분부터 왜 범인이 저러는 걸까? 의문을 가지면서 영화를 보게 된다.


정말 재미를 위한 영화였다면, 영화 마지막에서 그 답을 보여 준다.

가장 마지막에 형사가 그동안 속았으며, 범인이 승리하는 시나리오가 관객들에게 큰 반전으로 다가 올수도 있었기 때문이다.


하지만 영화의 중반부에서 그동안 자백한 사건들을 강압과 뇌물에 의한 자백으로 무죄를 만들고, 입건된 사건도 같은 이유로 무죄를 만들기 위한다는 사실을 알려준다.


그 사실을 아는데도 형사는 계속 수사를 하고, 그 과정에서 뇌물을 주면서 범인의 시나리오대로 흘러가는 답답한 서사를 보여준다.
그리고 범인이 무죄를 선고 받고 버스에 오르면서 외치는 '사필귀정'
모든 일들은 반드시 바르게 돌아 온다는 말을 아이러니하게도 범인이 얘기를 한다.

이 영화에서 범인이 외치는 사필귀정만큼 어이없는 일들이 영화 속에서 발생하는데...

1. 범인은 살인을 하였으나, 고작 15년형을 선고받고 살고 있었다는 점
2. 그게 하필이면 경찰이 증거를 조작해서 감형을 받았다는 점
3. 실적에 쫓겨 미제 사건을 조사하지 않은 경찰
(증거 조작도 결국 실적에 쫓겨서 일어난 일..)
4. 존속살인(아버지)의 공소시효가 지나서 법적으로 따지지 못한다는 말에 모든 것을 얘기하는 범인의 누나





최근 우리나라의 부조리한 이슈와 대입할 만한 사건들을 나열하고, 형사가 하는 일의 정당성을 같은 시대를 사는 관객들과 공감하고자 노력했다.
개인적으로 나는 형사의 이 대사에서 범인 뜻대로 진행하는 것의 정당성을 인정했다.

본인이 15년 뒤에 퇴직하는데 15년 뒤에 범인이 출소한다. 
그 때 다시 사회에 범죄를 일으킨다해도 형사 본인이 할 수 있는게 없다.
그래서 속는 거 알지만 끝까지 해본다.


형사의 끈질긴 조사 끝에 결정적인 증거를 찾게 되고 범인은 형사의 소망대로 영원히 사회와 격리되며 영화는 마무리된다.
말 그대로 사필귀정

연기를 잘한다는 배우들이 주연이라 몰입도가 좋았고, 실화를 기반으로 하였기에 잔인한 장면을 넣지 않았다.
자동차에 치이는 장면도 소리로만 묘사하고 빠르게 지나간다.

주지훈의 미친 연기력 돋보이는 영화였는데, 

동정심이 하나도 안 느껴지는 미친 범죄자 연기를 너무 잘해서, 통쾌함이 더했는거 같다.


오랜만에 극장에서 보는 영화인데, 속시원하게 보고 온거 같다.


PS 1) 

부산 사투리가 심한 영화다. 진짜 부산거리인 줄 알았다.

울산 사람인 나는 아무 문제없이 영화를 봤는데, 서울 사람인 감자님은 한마디도 못 알아 듣겠다고 표정이 안 좋았다.


PS 2)

햇빛을 비추면 썬글라스가 되는 안경이 너무 신기했다.

주지훈이 거의 영화 내내 쓰고 다녔으니 광고 효과가..

'눈누난냐 > 영화' 카테고리의 다른 글

[영화] 도어락 리뷰  (3) 2018.12.11
[영화] 보헤미안 랩소디 후기  (6) 2018.12.11
,

요새도 감자님과 만나면 평양냉면집을 찾아가곤 합니다.

평양냉면이라는 같은 범주안에 있지만, 가게마다 맛이 전부다 다르기 때문에 찾아다니는 맛이 있는거 같아요.

이번 주말에는 강남역에 있는 을밀대를 찾아갔어요.

옆집이 리모델링 중이라 페인트 냄새가 거슬렸지만, 선풍기와 환풍기를 틀어서 이내 냄새가 금방 없어지긴했어요.



감자님이랑 저는 당연히 물냉면을 2개 주문했어요. 

단골분들은 주문하는 방법이 따로 있다고 하는데 처음 와본 곳이라 그냥 기본으로 주문했어요.

살얼음이 있는 냉면을 선호하지 않는다면 살얼음을 빼달라고 하면 살얼음 없는 냉면을 드실 수 있어요.


따뜻한 육수를 주는데, 다른 평양 냉면 집에서 마시는 면수와 달리 육향이 나는 육수를 줘서 기대를 많이 했어요. 



그런데 살얼음이 이렇게 많을 줄은 몰랐네요 ㅋㅋㅋㅋ 

물론 먹다가 보면 잘 녹기도 하고 시원해서 좋긴했지만 

국물을 먹기 위해서 평양냉면을 먹는 감자님은 거슬려서 먹기 힘들어 하더라고요.

면은 약간 거칠지만 가위질 없이 끊어져요.


면을 풀기 전의 국물 맛과 면을 풀고 나서 국물 맛이 비슷한데, 을밀대 고유의 육수 향이 강해요.

그래서 면과 육수를 같이 먹어도 메밀향보다 육수 향이 더 강하게 느껴져요,

감자님은 육포향이 난다고 하는데, 저는 계속 치토스가 떠올랐어요. 치토스 불고기맛을 먹는다는 느낌??

밍밍하지도 않고 계속 코와 혀에 느껴지는 이 향이 뭘까 계속 고민하면서 먹게 되더라고요.



분명히 호불호가 있을거 같아요. 정말 밍밍한 평양냉면 스타일을 좋아한다면 분명히 좋은 선택은 아니에요.

우래옥과 필동면옥처럼 육수에 향이 강한 냉면이니깐요.

하지만 평양냉면이 처음이라면 이 걸로 시작해보는건 좋은거 같아요.


점심 저녁 식사 선택에 조금이나마 도움이 되었으면 좋겠네요.

감사합니다.



,