이 프로젝트는 동아리 KHUDA에서 진행하였습니다.

 

1. 연구 배경

 카카오톡을 이용하는 많은 사람들이 카카오톡 이모티콘을 사용한다. 이모티콘은 중요한 감정표현의 수단이다. 현재 수많은 이모티콘이 존재하지만 역설적으로 이모티콘 선택 과정에 어려움이 발생한다. 이에 우리 팀은 텍스트를 통해 감정을 분석하고 이미지 데이터 기반으로 이모티콘을 추천해주는 시스템을 개발하고자 했다.

이미 '카카오톡 이모티콘 플러스'라는 서비스가 존재한다. 채팅창에 단어만 입력해도 상황에 딱 맞는 이모티콘을 추천해준다. 이 서비스의 사용자들을 대상으로 설문하고 연구한 논문에 따르면 다음과 같다.

이 설문 결과를 통해 사용자의 취향상황에 맞는 이모티콘을 추천하는 것이 중요함을 느꼈다.

 

우리 팀은 컨텐츠 기반 필터링을 바탕으로 개발하고자 했다.

Content-based Filtering란?

사용자가 소비한 아이템에 대해 아이템의 내용(content)이 비슷하거나 특별한 관계가 있는 다른 아이템을 추천하는 방법을 말한다.

아이템이 유사한지 확인하려면 아이템의 비슷한 정도(유사도)를 수치로 계산할 수 있어야 한다. 유사도 계산을 위해서 일반적으로 아이템을 벡터 형태로 표현하고 이들 벡터간의 유사도 계산 방법을 많이 활용한다.

 

표현해야 하는 데이터 범주의 영역이 넓거나 이미지와 같이 복잡한 데이터인 경우는 데이터를 고정된 크기의 벡터로 표현하는 임베딩(Embedding) 방법을 많이 사용한다.

 

우리 팀이 개발하고자 하는 서비스 모델은 다음과 같다.

이모티콘을 쓰는 사람 중 대부분은 자신이 주로 사용하는 이모티콘이 하나씩은 있을 것이다. 사용자가 기존에 사용했던 이모티콘을 input에 넣어 그림체가 유사한 이모티콘을 선별하고자 한다.

조금 더 자세한 모델 구조는 다음과 같다.

 

 

 

2. 이미지 데이터

Style Transfer의 Gram Matrix

[이론]

스타일(style)은 서로 다른 특징(feature)간의 상관관계(correlation)을 의미한다. Gij는 특징 i와 특증 j의 상관관계이다.

 

Gram Matrix(구글에 검색하면 잘 나와있으니 설명은 생략한다.)

 

[코드]

Code를 조금 살펴 보겠다. (현재 코드 원본 파일이 날라가 ppt 자료를 참고 했습니다. ㅠㅠ)

pre-trained된 ResNet을 사용한다.

ResNet(Residual neural network)는 Skip connection 방식을 사용한다. 따라서 깊이(layer)가 깊어졌을 때 발생하는 문제를 해결할 수 있다.

그리고 shape 조절 후 list에 array를 저장했다.

그런 후 ResNet 모델을 가져와 feature 정보를 추출하고 feature 행렬을 고차원에서 2차원으로 차원 축소를 하였다. 차원 축소를 한 이유는 시각화를 위해서다.

가장 가까운 이웃 3개를 계산한 후 시각화했다. 그 결과는 아래와 같다.

 

참고로 이번 프로젝트에서 사용한 이모티콘은 총 50개로, 최대한 다양하게 인기 순위에서 임의로 선정하였다.

선정한 이모티콘들

 

[결과]

 

 

 

 

[RGB Scale]

 

  • 색깔을 더 중요하게 반영하는 경향이 있음.
  • 비슷한 그림체이지만 색깔이 다르면 잡아 내지 못함.
  • 직관적으로 납득이 가지 않는 부분이 존재함.

 

 

 

 

 

 

 

 

 

 

 

[Gray Scale]

 

  • 색깔을 더 중요하게 반영하는 문제를 해결함.
  • 하지만, 그림체의 특징을 추출하는 성능이 저하함.
  • 여전히 직관적으로 납득이 가지 않는 부분이 존재함.

 

 

 

 

 

 

 

[문제점]

Covariance이므로 상대적이다. 즉, 매번 계산을 할 때마다 테스트 데이터를 포함하여 covariance를 계산해야 한다. 이를 2차원으로 mapping하는 t-SNE 과정이 오래걸리고 번거롭다. 또한 오래된 방법이라 성능이 뛰어나지 못하다.(직관적으로 납득이 가지 않는 부분이 존재한다.)

 

CLIP(Contrastive Learning In Pretraining)

[이론]

 

CLIP은 4억개의 (이미지, 텍스트) 쌍으로 학습된 모델이다. (이미지, 물체 분류) 데이터를 사용하는 다른 모델과는 달리 (이미지, 텍스트) 데이터를 사용하는 것이 특징이다. 전자의 경우에는 label이 지니는 정보의 양이 너무 적다는 단점이 있다. 이미지에 포함된 다양한 정보들 중 물체의 '종류'라는 단일 정보만을 표현하기 때문에 이미지가 가진 다양한 특징을 활용하는 데에는 한계가 있다. 그래서 우리 팀은 CLIP을 사용하고자 했다. 

우리 팀은 이모티콘의 썸네일의 특징 추출을 할 것이기 때문에 이미지 인코더 부분만 사용했다.

N개의 (텍스트, 이미지) 쌍의 배치가 주어진다고 할 때, CLIP은 그림에서 보다시피 N*N의 가능한 (이미지, 텍스트) 쌍을 예측하도록 학습된다. 따라서 만약 대각선에 위치한 positive pair의 코사인 유사도를 최대화 하고, 나머지 쌍들(negative pair)은 코사인 유사도를 최소화 하는 방향으로 Cross Entropy loss를 사용해 학습한다.

 

VIT(Vision Transformer)란?

NLP 분야에서 사용하는 Transformer를 Image Classification 분야에 맞게 약간 변형한 것이다.

이미지를 Patch로 분할 후 Sequence로 입력다. 이는 NLP에서 단어가 입력되는 방식과 동일하다.

 

[코드]

 

[결과]

이미지 오른쪽 상단에 루피(결과: 파란색)와 사람 이미지(결과: 빨간색)를 입력으로 넣은 결과이다. 유사도가 가장 높은 5개를 체크 해두었다.

루피에 대한 결과를 보면 먼저 루피와 똑같은 시리즈인 이모티콘들은 모두 유사도가 가장 높게 나왔고 루피와 비슷한 포즈를 한 동물들이 그 다음으로 높게 나온 것을 확인할 수 있다.

사람 이미지에 대한 결과를 보면 똑같은 실제 사람 이모티콘의 유사도가 가장 높게 나온 것을 확인할 수 있고 실제 사람은 아니지만 사람 형태를 한 이모티콘이 그 다음으로 높게 나온 것을 확인할 수 있다.

 

이후 투표 시스템을 추가했다.

 

 

 

3. 텍스트 데이터

  • 감정에 맞게 이모티콘을 추천해주는 것이 주제이므로 다중 분류 모델로 학습을 진행했다.
  • 충분한 대화 데이터 셋 확보가 어려워 사전 훈련된 모델을 사용했다.
  • 사전 학습 모델 중 한국어로 학습한 koBERT 모델이 가장 성능이 좋다.

 

[데이터 수집]

아래를 참고해 총 84MB 가량의 데이터를 확보했다.

1. 한국어 감정 정보가 포함된 단발성 대화 데이터셋

2. 한국어 감정 정보가 포함된 연속적 대화 데이터셋

3. 감성대화 말뭉치

4. 온라인 구어체 말뭉치 데이터

 

[데이터 전처리]

  • 중복되는 감정을 통합함.
  • json 파일에서 필요한 내용만 추출하여 정리함.
  • 필요없는 column은 제거함.

 

[데이터 증강]

라벨링이 되지 않은 데이터를 ChatGPT API를 이용해 라벨링을 진행했다.

Data augmentation: I want you to classify this text to '불안, 당황, 분노, 슬픔, 중립, 행복, 혐오', the text is '%sentence_input

그 결과 1.1MB의 데이터를 추가적으로 확보할 수 있었다.

 

[코드]

pre-trained된 KoBERT 모델을 이용했다. 분류하려는 감정 개수, 클래스 수를 6으로 설정 했다.

KoBERT에 대한 내용은 다음의 링크를 참고해 주세요. (https://velog.io/@seolini43/KOBERT%EB%A1%9C-%EB%8B%A4%EC%A4%91-%EB%B6%84%EB%A5%98-%EB%AA%A8%EB%8D%B8-%EB%A7%8C%EB%93%A4%EA%B8%B0-%ED%8C%8C%EC%9D%B4%EC%8D%ACColab)

 

[결과 - 1]

 

매 epoch마다 모델 학습 결과를 저장했고 Epoch 3 이후 test acc가 감소하여 학습을 중단 시켰다. 가장 test acc가 높게 나온 epoch3의 모델을 활용했다.

 

1차 결과

 

  • '혐오'의 경우가 잘 드러나지 않았다. '혐오'를 '분노'에 통합시켰다.
  • test acc가 0.7 정도로 train acc에 비해 낮았다.
  • 행복 데이터를 기준으로 데이터 비율 재수정 후 2차학습을 진행했다.

 

[결과 - 2]

이번에도 epoch 3 이후에 test acc가 감소했고 가장 높게 나온 epoch3의 모델을 활용했다. 모델에 텍스트를 입력한 결과는 다음과 같다.

 

[한계점]

  • 데이터 비율이 고르지 못했다.
  • ChatGPT로 데이터 증강을 시도했으나 과연 잘 된 것인지 검증을 하지 못했다.
  • 이모티콘 라벨링 과정에서 부정적인 감정이 별로 없었다.

 

 

4. 최종 결과

최종 결과는 가독성을 위해 ppt 자료를 이용 하겠다.

[Example 1]

가장 먼저 실사 인물 형태의 이모티콘을 이미지 input으로 줬을 때의 이미지 추천 결과이다. 

그리고 텍스트 input을 입력하고 난 뒤 최종적으로 선정된 이모티콘들은 다음과 같다.

 

 

[Example 2]

두번째 예시로는 실제 사람은 아니지만 인물 형태의 이모티콘을 input으로 넣었다. 텍스트를 입력한 뒤 나온 최종 결과는 아래와 같다.

 

 

[Example 3]

마지막으로는 동물 형태의 이모티콘을 주로 사용한다고 가정하였다. 결과는 다음과 같다.

 

5. Review

 생각보다 좋은 결과가 나온 것 같아 뿌듯했다. 나는 이미지 데이터를 주로 맡아 개발을 했다. 약 2주라는 정말 짧은 시간 안에 프로젝트를 진행해야 했던 터라 이론을 완벽하게 공부하지 못하고 진행한 면이 없지 않아 있다. 추후 다시 프로젝트를 리뷰하며 이론을 확실히 하는 시간을 가지면 좋을 것 같다. 팀원들이 다양한 분야를 희망하고 있었기 때문에 추천 시스템, 컴퓨터비전, 자연어 처리 분야를 모두 섞은 프로젝트가 된 것 같다. 세 분야에 대해 경험과 지식이 없었던 나로써는 정말 좋은 경험이 되지 않았나 싶다. 당장은 이 분야들을 공부할 계획은 없지만 나중에 공부하게 된다면 아마 이번 프로젝트가 도움이 되어 좀 더 수월하게 공부할 수 있을 것이라 생각한다.

다음은 책 '모두의 딥러닝'의 부록 부분에 해당하는 내용입니다.

 

모두의 딥러닝 - YES24

2년 연속 베스트셀러, 내용과 소스 코드 전면 업그레이드딥러닝 기초부터 최신 트렌드까지 한 권으로 공부하자그동안 딥러닝 입문서로 제 역할을 톡톡히 해낸 『모두의 딥러닝』이 최근 주목받

www.yes24.com

 

 

M. 데이터 병합하기

# 실습을 위한 데이터 프레임 만들기
import pandas as pd

adf = pd.DataFrame({"x1" : ['A', 'B', 'C'],
                    "x2" : [1, 2, 3]})

bdf = pd.DataFrame({"x1" : ['A', 'B', 'D'],
                    "x3" : ['T', 'F', 'T']})

cdf = pd.DataFrame({"x1" : ['B', 'C', 'D'],
                    "x3" : ['2', '3', '4']})

실행 결과(adf)
실행 결과(bdf)
실행 결과(cdf)

70. 왼쪽 열을 축으로 병합하기

# x1을 키로 해서 병합, 왼쪽(adf)을 기준으로
# 왼쪽의 adf에는 D가 없으므로 해당 값은 NaN으로 변한한다.
pd.merge(adf, bdf, how='left', on='x1')

실행 결과

71. 오른쪽 열을 축으로 병합하기

# x1을 키로 해서 병합, 오른쪽(bdf)을 기준으로
# 왼쪽의 bdf에는 C가 없으므로 해당 값은 NaN으로 변한한다.
pd.merge(adf, bdf, how ='right', on='x1')

실행 결과

72. 공통 값만 병합하기

pd.merge(adf, bdf, how='inner', on='x1')

실행 결과

73. 모든 값을 병합하기

pd.merge(adf, bdf, how='outer', on='x1')

실행 결과

74. 특정한 열을 비교해서 공통 값이 존재하는 경우만 가져오기

# adf와 bdf의 특정한 열을 비교해서 공통 값이 존재하는 경우만 가져온다.
# adf.x1열과 bdf.x1열은 A와 B가 같으므로 adf의 해당 값만 출력한다.
adf[adf.x1.isin(bdf.x1)]

실행 결과

75. 공통 값이 존재하는 경우 해당 값을 제외하고 병합하기

adf[~adf.x1.isin(bdf.x1)] # 해당 값만 빼고 출력한다.

실행 결과

76. 공통 값이 있는 것만 병합하기

pd.merge(adf, cdf)

실행 결과

77. 모두 병합하기

pd.merge(adf, cdf, how='outer')

실행 결과

78. 어디서 병합되었는지 표시하기

pd.merge(adf, cdf, how='outer', indicator=True)

실행 결

79. 원하는 병합만 남기기

pd.merge(adf, cdf, how='outer', indicator=True).query('_merge=="left_only"')

실행 결과

80. merge 칼럼 없애기

pd.merge(adf, cdf, how='outer', indicator=True).query('_merge=="left_only"').drop(columns=['_merge'])

실행 결과

 

 

N. 데이터 가공하기

df = pd.DataFrame({"a" : [4, 5, 6, 7], # 열 이름을 지정해  주고 시리즈 형태로 데이터 저장
                   "b" : [8, 9, 10, 11],
                   "c" : [12, 13, 14, 15]},
                  index=[1, 2, 3, 4]) # 인덱스는 1, 2, 3, 4으로 정해 준다.

실행 결

81. 행 전체를 한 칸 아래로 이동하기

df.shift(1)

실행 결과

82. 행 전체를 한 칸 위로 이동하기

df.shift(-1)

실행 결과

83. 첫 행부터 누적해서 더하기

df.cumsum()

실행 결과

84. 새 행과 이전 행을 비교하면서 최댓값 출력하기

df.cummax()

실행 결과

85. 새 행과 이전행을 비교하면서 최솟값 출력하기

df.cummin()

실행 결과

86. 첫 행부터 누적해서 곱하기

df.cumprod()

실행 결과

 

O. 그룹별로 집계하기

import pandas as pd

# 실습을 위해 데이터를 불러온다.
!git clone https://github.com/taehojo/data.git

# 15장의 주택 가격 예측 데이터를 불러온다.
df = pd.read_csv("./data/house_train.csv")

실행 결과

87. 그룹 지정 및 그룹별 데이터 수 표시

df.groupby(by="YrSold").size() # 팔린 연도를 중심으로 그룹을 만든 후 그룹별 수 표시

실행 결과

88. 그룹 지정 후 원하는 칼럼 표시하기

# 팔린 연도를 중심으로 그룹을 만든 후 각 그룹별로 주차장의 넓이를 표시한다.
df.groupby(by="YrSold")['LotArea'].mean()

실행 결과

89. 밀집도 기준으로 순위 부여하기

df['SalePrice'].rank(method='dense') # 각 집 값은 밀집도를 기준으로 몇 번째인가

실행 결과

 

90. 최젓값을 기준으로 순위 부여하기

df['SalePrice'].rank(method='min') # 각 집 값이 최젓값을 기준으로 몇 번째인가

실행 결과

91. 순위를 비율로 표시하기

# 집 값의 순위를 비율로 표시한다(0=가장 싼 집, 1=가장 비싼 집).
df['SalePrice'].rank(pct=True)

실행 결과

92. 동일 순위에 대한 처리 방법 정하기

# 순위가 같을 때 순서가 빠른 사람을 상위로 처리한다.
df['SalePrice'].rank(method='first')

실행 결과

 

다음은 책 '모두의 딥러닝'의 부록 부분에 해당하는 내용입니다.

 

모두의 딥러닝 - YES24

2년 연속 베스트셀러, 내용과 소스 코드 전면 업그레이드딥러닝 기초부터 최신 트렌드까지 한 권으로 공부하자그동안 딥러닝 입문서로 제 역할을 톡톡히 해낸 『모두의 딥러닝』이 최근 주목받

www.yes24.com

 

 

I. 새로운 열 만들기

# 새로운 열 만들기 실습을 위한 데이터 프레임 만들기
import pandas as pd

df = pd.DataFrame(
    {"a":[4, 5, 6, 7],
     "b":[8, 9, 10, 11],
     "c":[12, 13, 14, 15]},
     index=pd.MultiIndex.from_tuples(
         [('d', 1), ('d', 2), ('e', 1), ('e', 2)],
         names=['n', 'v']))

실행 결과

52. 조건에 맞는 새 열 만들기

df['sum'] = df['a'] + df['b'] + df['c']

실행 결과

53. assign()을 이용해 조건에 맞는 새 열 만들기

# a열, b열, c열의 값을 모두 더해 d열을 만들어 준다.
df = df.assign(multiply=lambda df: df['a']*df['b']*df['c'])

실행 결과

54. 숫자형 데이터를 구간으로 나누기

# a열을 두 개로 나누어 각각 새롭게 레이블을 만들라는 의미이다.
df['qcut'] = pd.qcut(df['a'], 2, labels=["600이하", "600이상"])

실행 결과

55. 기준 값 이하와 이상을 모두 통일시키기

# a열에서 5 이하는 모두 5로, 6 이상은 모두 6으로 변환한다.
df['clip'] = df['a'].clip(lower=5, upper=6)

실행 결과

56. 최댓값 불러오기

df.max(axis=0) # axis=0은 행과 행 비교, axis=1은 열과 열 비교

실행 결과

57. 최솟값 불러오기

df.min(axis=0)

실행 결과

 

J. 행과 열 변환하기

# 실습을 위해 새로운 데이터를 만든다.
df = pd.DataFrame({'A': {0: 'a', 1: 'b', 2: 'c'},
                   'B': {0: 1, 1: 3, 2: 5},
                   'C': {0: 2, 1: 4, 2: 6}})

실행 결과

58. 모든 열을 행으로 변환하기

pd.melt(df)

실행 결과

59. 하나의 열만 행으로 이동시키기

pd.melt(df, id_vars=['A'], value_vars=['B']) # A열만 그대로, B열은 행으로 이동

실행 결과

60. 여러 개의 열을 행으로 이동시키기

# A열만 그대로, B열과 C열은 행으로 이동시킨다.
df_melt = pd.melt(df, id_vars=['A'], value_vars=['B', 'C'])

실행 결과

61. 특정 열의 값을 기준으로 새로운 열 만들기

# A열을 새로운 인덱스로 만들고, B열과 C열은 이에 따라 정리한다.
df_pivot = df_melt.pivot(index='A', columns='variable', values='value')

실행 결과

62. 원래 데이터 형태로 되돌리기

df_origin = df_pivot.reset_index() # 인덱스를 리셋한다.
df_origin.columns.name = None      # 인덱스 열의 이름을 초기화한다.

실행 결과

 

K. 시리즈 데이터 연결하기

# 시리즈 데이터 만들기
s1 = pd.Series(['a', 'b'])
s2 = pd.Series(['c', 'd'])

실행 결과(s1)
실행 결과(s2)

63. 시리즈 데이터 합치기

pd.concat([s1, s2])

실행 결과

64. 데이터를 병합할 때 새로운 인덱스 만들기

pd.concat([s1, s2], ignore_index=True)

실행 결과

65. 계층적 인덱스를 추가하고 열 이름 지정하기

pd.concat([s1, s2],
          keys=['s1', 's2'],
          names=['Series name', 'Row ID'])

실행 결과

 

L. 데이터 프레임 연결하기

# 데이터 프레임 병합 실습을 위한 데이터 프레임 1
df1 = pd.DataFrame([['a', 1],
                    ['b', 2]],
                    columns=['letter', 'number'])
                    
# 데이터 프레임 2
df2 = pd.DataFrame([['c', 3],
                    ['d', 4]],
                    columns=['letter', 'number'])
                    
# 데이터 프레임 3
df3 = pd.DataFrame([['c', 3, 'cat'],
                    ['d', 4, 'dog']],
                    columns=['letter', 'number', 'animal'])
                    
# 데이터 프레임 4
df4 = pd.DataFrame([['bird', 'polly'],
                    ['monkey', 'george']],
                    columns=['animal', 'name'])

실행 결과(df1)
실행 결과(df2)
실행 결과(df3)
실행 결과(df4)

66. 데이터 프레임 합치기

pd.concat([df1, df2])

실행 결과

67. 열의 수가 다른 두 데이터 프레임 합치기

pd.concat([df1, df3])

실행 결과

68. 함께 공유하는 열만 합치기

pd.concat([df1, df3], join="inner")

실행 결과

69. 열 이름이 서로 다른 데이터 합치기

pd.concat([df1, df4], axis=1)

실행 결과

다음은 책 '모두의 딥러닝'의 부록 부분에 해당하는 내용입니다.

 

모두의 딥러닝 - YES24

2년 연속 베스트셀러, 내용과 소스 코드 전면 업그레이드딥러닝 기초부터 최신 트렌드까지 한 권으로 공부하자그동안 딥러닝 입문서로 제 역할을 톡톡히 해낸 『모두의 딥러닝』이 최근 주목받

www.yes24.com

 

 

E. 행과 열 추출하기

28. 특정 행과 열을 지정해 가져오기

# df.loc[가져올 행, 가져올 열] 형태로 불러온다.
df.loc[:, 'a':'c'] # 모든 인덱스에서 a열부터 c열까지 가져오라는 의미이다.

실행 결과

29. 인덱스로 특정 행과 열 가져오기

# 0 인덱스부터 2 인덱스까지 0번째 열과 2번째 열을 가져오라는 의미(첫 열이 0번째)
df.iloc[0:3, [0, 2]]

실행 결과

30. 특정 열에서 조건을 만족하는 행과 열 가져오기

# a열 값이 5보다 큰 경우 a열과 c열을 출력하라는 의미이다.
df.loc[df['a']>5, ['a', 'c']]

실행 결과

31. 인덱스를 이용해 특정 조건을 만족하는 값 불러오기

df.iat[1, 2] # 1번째 인덱스에서 2번째 열 값을 가져온다.

# 실행결과
# 13

 

F. 중복 데이터 다루기

# 실습을 위해 중복된 값이 포함된 데이터 프레임을 만든다.
df = pd.DataFrame(
    {"a":[4, 5, 6, 7, 7],
     "b":[8, 9, 10, 11, 11],
     "c":[12, 13, 14, 15, 15]},
     index=pd.MultiIndex.from_tuples(
         [('d', 1),('d', 2),('e', 1),('e', 2),('e', 3)],
         names=['n', 'v']))

실행 결과(df)

32. 특정 열에 어떤 값이 몇 개 들어 있는지 알아보기

df['a'].value_counts()

실행 결과

33. 데이터 프레임의 행이 몇 개인지 세어 보기

len(df)

# 실행 결과
# 5

34. 데이터 프레임의 행과 열이 몇 개인지 세어 보기

df.shape

# 실행 결과
# (5, 3)

35. 특정 열에 유니크한 값이 몇 개인지 세어 보기

df['a'].nunique()

# 실행 결과
# 4

36. 데이터 프레임의 형태 한눈에 보기

df.describe()

실행 결과

37. 중복된 값 제거하기

df = df.drop_duplicates()

실행 결과

 

G. 데이터 파악하기

38. 각 열의 합 보기

df.sum()

실행 결과

39. 각 열의 값이 모두 몇 개인지 보기

df.count()

실행 결과

40. 각 열의 중간 값 보기

df.median()

실행 결과

41. 특정 열의 평균값 보기

df['b'].mean()

# 실행 결과
# 9.5

42. 각 열의 25%, 75%에 해당하는 수 보기

df.quantile([0.25, 0.75])

실행 결과

43. 각 열의 최솟값 보기

df.min()

실행 결과

44. 각 열의 최댓값 보기

df.max()

실행 결과

45. 각 열의 표준편차 보기

df.std()

실행 결과

46. 데이터 프레임 각 값에 일괄 함수 적용하기

import numpy as np
df.apply(np.sqrt) # 제곱근 구하기

실행 결과

 

H. 결측치 다루기

# 넘파이 라이브러리를 이용해 null 값이 들어 있는 데이터 프레임 만들기

df = pd.DataFrame(
    {"a":[4, 5, 6, np.nan],
     "b":[7, 8, np.nan, 9],
     "c":[10, np.nan, 11, 12]},
     index=pd.MultiIndex.from_tuples(
         [('d', 1), ('d', 2), ('e', 1), ('e', 2)],
         names=['n', 'v']))

실행 결과(df)

47. null 값인지 확인하기

isnull : 결측값이면 True 반환, 정상값이면 False 반환. df.isna( )와 동일함.
pd.isnull(df)

실행 결과

48. null 값이 아닌지 확인하기

notnull : 결측값이면 False 반환, 정상값이면 True 반환. df.notna( )와 동일함.
pd.notnull(df)

실행 결과

49. null 값이 있는 행 삭제하기

기본 사용법

df.dropna(axis=0, how='any', thresh=None, subset=None, inplace=False)

axis : {0: index / 1: columns} 결측치 제거를 진행 할 레이블이다.

how : {'any' : 존재하면 제거 / 'all' : 모두 결측치면 제거} 제거할 유형이다. 포함만 시켜도 제거할지, 전부 NA여야 제거할지 정할 수 있다.

thresh : 결측값이 아닌 값이 몇 개 미만일 경우에만 적용시키는 인수이다.

subset : dropna 메서드를 수행할 레이블을 지정한다.

inplace : 원본을 변경할지의 여부이다.

 

df_notnull = df.dropna()

실행 결과

50. null 값을 특정 값으로 대체하기

df_fillna = df.fillna(13) # 13으로 대체하는 예

실행 결과

51. null 값을 특정 계산 결과로 대체하기

df_fillna_mean = df.fillna(df['a'].mean()) # a열의 평균값으로 대체한다.

실행 결과

다음은 책 '모두의 딥러닝'의 부록 부분에 해당하는 내용입니다.

 

모두의 딥러닝 - YES24

2년 연속 베스트셀러, 내용과 소스 코드 전면 업그레이드딥러닝 기초부터 최신 트렌드까지 한 권으로 공부하자그동안 딥러닝 입문서로 제 역할을 톡톡히 해낸 『모두의 딥러닝』이 최근 주목받

www.yes24.com

 

 

판다스(pandas)란?

판다스(pandas)는 데이터 분석과 관련된 다양한 기능을 제공하는 파이썬 라이브러리이다.

데이터를 쉽게 조작하고 다룰 수 있도록 도와주기 때문에 딥러닝, 머신 러닝을 공부할 때 많이 쓰인다.

 

A. 데이터 만들기

1. 판다스 라이브러리 불러오기

import pandas as pd

2. 데이터 프레임 만들기

df = pd.DataFrame(        # df라는 변수에 데이터 프레임을 담아 준다.
    {"a" : [4, 5, 6, 7],  # 열 이름을 지정해 주고 시리즈 형태로 데이터를 저장한다.
     "b" : [8, 9, 10, 11],
     "c" : [12, 13, 14, 15]},
     index=[1, 2, 3, 4])  # 인덱스는 1, 2, 3으로 정해 준다.

3. 데이터 프레임 출력하기

df # 데이터 프레임의 이름을 입력해 출력한다.

실행 결과

4. 데이터의 열 이름을 따로 지정해서 만들기

df = pd.DataFrame(        
    [[4, 8, 12],  
     [5, 9, 13],
     [6, 10, 14],
     [7, 11, 15]],
     index=[1, 2, 3, 4],
     columns=['a', 'b', 'c'])

실행 결과

5. 인덱스가 두 개인 데이터 프레임 만들기

df = pd.DataFrame(       
    {"a" : [4, 5, 6, 7],  
     "b" : [8, 9, 10, 11],
     "c" : [12, 13, 14, 15]},
     index=pd.MultiIndex.from_tuples( # 인덱스를 튜플로 지정한다.
         [('d', 1), ('d', 2), ('e', 1), ('e', 2)],
         names=['n', 'v']))           # 인덱스 이름을 지정한다.

실행 결과

 

B. 데이터 정렬하기

6. 특정 열 값을 기준으로 정렬하기

df.sort_values('a', ascending=False) # ascending=False를 적어 주면 역순으로 정렬

실행 결과

7. 열 이름 변경하기

df.rename(columns={'c':'d'}) # c 열 이름을 d로 변경한다.

실행 결과

8. 인덱스 값 초기화하기

df.reset_index()

실행 결과

9. 인덱스 순서대로 정렬하기

df.sort_index()

실행 결과

10. 특정 열 제거하기

df.drop(columns=['a', 'b'])

실행 결과

 

C. 행 추출하기

11. 맨 위의 행 출력하기

df.head(2) # 2행을 출력한다.

실행 결과

12. 맨 아래 행 출력하기

df.tail(2) # 2행을 출력한다.

실행 결과

13. 특정 열의 값을 추출하기

df[df["a"] > 4] # a열 중 4보다 큰 값이 있을 경우 해당 행을 추출한다.

실행 결과

14. 특정 열에 특정 값이 있을 경우 추출하기

df[df["a"] == 6] # a열 중 6이 있을 경우 해당 행을 추출한다.

실행 결과

15. 특정 열에 특정 값이 없을 경우 추출하기

df[df["a"] != 5] # a열 중 5가 없을 경우 해당 행을 추출한다.

실행 결과

16. 특정 열에 특정 숫자가 있는지 확인하기

df[df['a'].isin([4])] # 원하는 숫자를 리스트([int]) 형식으로 써 준다.

실행 결과

17. 특정 비율로 데이터 샘플링하기

df.sample(frac=0.75) # 실행할 때마다 정해진 비율만큼 랜덤하게 추출한다.

실행 결과

18. 특정 개수만큼 데이터 샘플링하기

df.sample(n=3) # 실행할 때마다 n에서 정한 만큼 랜덤하게 추출한다.

실행 결과

19. 특정 열에서 큰 순서대로 불러오기

df.nlargest(3, 'a') # a열에서 큰 순서대로 세 개를 불러온다.

실행 결과

20. 특정 열에서 작은 순서대로 불러오기

df.nsmallest(3, 'a') # a열에서 작은 순서대로 세 개를 불러온다.

실행 결과

 

D. 열 추출하기

21. 인덱스의 범위로 불러오기

# 0부터 세므로 첫 번째 줄은 인덱스 0, 4번째 줄은 인덱스 3이 된다.
df.iloc[1:4] # [a:b]의 경우 a 인덱스부터 b-1 인덱스까지 불러오라는 의미이다.
             # a열에서 순서대로 세 개를 불러온다.

실행 결과

22. 첫 인덱스를 지정해 불러오기

df.iloc[2:] # [a:]는 a 인덱스부터 마지막 인덱스까지 불러오라는 의미이다.

실행 결과

23. 마지막 인덱스를 지정해 불러오기

df.iloc[:3] # [:b]는 처음부터 b-1 인덱스까지 불러오라는 의미이다.

실행 결과

24. 모든 인덱스 불러오기

df.iloc[:] # [:]는 모든 인덱스를 불러오라는 의미이다.

실행 결과

25. 특정열을 지정해 가져오기

df[['a', 'b']] # a열과 b열을 가져오라는 의미이다.

실행 결과

26. 조건을 만족하는 열 가져오기

df.filter(regex='c') # 열 이름에 c라는 문자가 포함되어 있으면 출력하라는 의미이다.

실행 결과

27. 특정 문자가 포함되지 않는 열 가져오기

# 열 이름에 c라는 문자가 포함되어 있지 않으면 출력하라는 의미이다.
df.filter(regex='^(?!c$).*')

실행 결과

해당 결과물에 대한 원리 및 자세한 내용(수행 과정, 그림판의 기능, 문제점 인식 및 해결 과정, 의의, 코드)은 모두 아래에 있는 pdf 파일(28페이지)을 참고해주시면 감사하겠습니다.

가상그림판_블로그용.pdf
0.94MB

 

 


기능

모드명[손가락을 폈는 지의 여부]

 

1. Selection Mode[0, 1, 1, 0, 0]

Selection Mdoe

 

2. Drawing Mode[0, 1, 0, 0, 0]

Drawing  Mode

 

3. All Erase Mode[0, 0, 0, 0, 0]

All Erase Mode

 

4. Palette On/Off Mode[1, 0, 0, 0, 0]

Palette On/Off Mode

 

5. Draw Rectangle Mode[1, 0, 0, 0, 1]

Draw Rectangle Mode

 

6. Draw Straight Mode[1, 1, 0, 0, 1]

Draw Straight Mode

 

7. Thickness Control Mode - Up[1, 1, 0, 0, 0] / Down[0, 1, 0, 0, 1] (검지를 swing)

Thickness Control Mode

 


시연 영상

 

개발이 끝난 후의 생각

기존의 다른 그림판들과는 달리 도구를 사용하지 않고 오직 '손'을 이용해 그림을 그린다는 점이 독특한 것 같습니다. 그렇지만 허공에서 쉬지 않고 손을 허우적대는 것은 생각보다 힘들었고 이에 대해 고민을 했습니다.

카메라의 위치와 각도를 조절해 책상과 같은 바닥에 있는 손을 비추게 하면 장시간 사용하더라도 무리가 없겠다고 결론을 냈습니다. 

 

저는 끊임없이 떠오르는 여러 기능들을 꾸준히 추가하고자 합니다. 기존의 윈도우 그림판처럼 RGB값에 해당하는 모든 색상을 구현할 수 있도록 하거나 음악적인 부분을 조금 더 강조하기 위해 Drawing할 때마다 소리가 (부드럽게 잘)나오도록 프로그램을 개선하는 것처럼 말입니다. 또한 현재 많은 기능들을 구현했다고 하더라도 아직 윈도우 그림판에 비해 기능이 부족한 면이 있습니다. 이또한 고려해서 개선시킬 예정입니다. 제 프로젝트를 봐주셔서 감사합니다.

+ Recent posts