본문 바로가기
## 오래된 게시글 (미관리) ##/Python (Linux)

10.Python - 데이터 샘플링

by #Glacier 2018. 11. 23.
반응형

시작하기 전에 한글로 된 폴더명을 영어로 바꿉시다.

export LANG=C

xdg-user-dirs-gtk-update


이제 주피터 노트북을 켜서 노트를 만듭니다.




이름은 Housing을 해주고!

그 유명한 Hello World!를 쳐봅니다..


 


파일을 불러들이는 방법은 여러개가 있겠죠.

이번엔 우리가 tgz파일을 Windows에서 다운로드하여 공유폴더를 통해 받아보는 방법을 해보겠습니다.


https://raw.githubusercontent.com/ageron/handson-ml/master/datasets/housing/housing.tgz

여기를 누르면 바로 다운로드 되는데요. (자료는 '핸즈온 머신러닝'에서 공개한 자료입니다.)

이를 우리가 바탕화면에서 공유폴더로 설정했던 mywork에 넣어서 CentOS에서 확인합니다.




요렇게 바깥으로 꺼냅니다.


mv housing.tgz /$HOME/ml

cd $ML_PATH

tar -xvf housing.tgz

하시면 풀립니다!

ml폴더 내에서 

jupyter notebook --allow-root

로 들어가시면 housing.csv가 보입니다.


거기서 이제 Housing이라는 Python3 노트를 만들어서

아래와 같이 작성합니다.



pandas를 통해 임포트 했는데요!

housing.head()

DataFrame의 head()메소드를 사용해 처음 다섯 행을 확인해보았습니다.

바를 움직여보시면 총 10개의 변수가 있습니다.

판다스는 기본적으로 데이터프레임 객체로 반환합니다.


.info() 메소드는 데이터에 대한 간략한 설명과 특히 전체 행 수, 각 특성의 Data Type, Null이 아닌 값의 개수를 확인하는 데 유용합니다.



보면 total_bedrooms는 20433개만 non-null이네요.

따라서 나머지 207개 구역은 이 특성을 가지고 있지 않은데, 책에서는 어떻게 처리할지 궁금하네용

또, ocean_proximity는 object형이라는 점을 볼 수 있습니다. 나머지는 float형이네요



housing["ocean_proximity"].value_counts() 

ocean_proximity 변수의 값을 세어보면, 내륙 6551개, Ocean 근처 2658개, Bay근처 2290개, 섬은 5개 데이터가 있습니다.

.describe()메소드는 숫자형 특성의 요약정보를 보여줍니다.

위/경도의 평균값, std(standard deviation) 표준편차, 최소값, 등 다양한 정보를 확인할 수 있으며, Null값은 제외됩니다.

25%, 50%, 75%는 percentile로 1사분위수, 2사분위수, 3사분위수로 말할 수 있습니다. 2사분위수는 중앙값으로 볼 수 있죠.


# 표준편차는 일반적으로 시그마로 표기하고, 평균에서부터 떨어진 거리를 제곱하여 평균한 분산의 제곱근이다.

  어떤 특성이 정규분포(가우시안 분포)를 따르는 경우 약 68%의 값은 1시그마 안에, 95%는 2시그마 안에, 

  99.7%는 3시그마 안에 존재합니다.


데이터의 형태를 빠르게 검토하는 다른 방법은 각 숫자형 특성을 히스토그램으로 보는 방법도 있습니다. 




# hist() 메서드는 matplotlib을 사용하고 결국 화면에 그래프를 그리기 위해 사용자 컴퓨터 그래픽 백엔드를 필요로 합니다.

 그래서 matplotlib이 사용할 백엔드를 지정해주어야 합니다. 그래서 매직명령을 사용하여 주피터 자체의 백엔드를 

 사용하도록 설정하는 것입니다. 그래서 노트북 안에 그려지게 됩니다. 주피터 노트북에서는 show() 메서드를 사용하는 

 것은 선택사항입니다. 주피터는 셀이 실행될 때 자동으로 그래프를 그려줍니다.

 

# 또한, IPython kernel 4.4.0부터 $matplotlib inline 매직명령을 사용하지 않더라고 matplotlib 1.5이상이면 자동으로

  주피터 자체 백엔드로 설정한다고 합니다.


그래서 실제로 매직명령어인 %matplotlib inline행과

plt.show()를 둘다 지우고 실행하셔도 똑같이 그래프가 나타남을 알 수 있습니다.



 

이제 각 데이터에 대한 가정입니다.

1. 먼저 Median Income(중간 소득) 특성이 US달러로 표현되어 있지 않습니다. 데이터를 취합한 팀이

스케일을 조정하고, 상한이 15(실제로는 15.0001), 하한이 0.5(실제로는 4.9999)가 되도록 만들었다고 가정합니다.

머신러닝에서는 전처리된 데이터를 다루는 경우가 흔하고, 이것이 문제가 되지는 않지만 데이터가 어떻게 계산된 것인지는

반드시 이해하고 있어야 합니다.


2. Housing Median Age(중간 주택 연도)와 Median House Value(중간 주택 가격) 역시 최댓값과 최솟값을 한정했습니다.

중간 주택 가격의 경우는 타깃 속성(레이블)으로 사용되기 때문에 심각한 문제가 될 수 있습니다. 가격이 한곗값을 넘어가지 않도록 

머신러닝 알고리즘이 학습될지도 모릅니다. 이것이 문제가 될 지 안될지는 클라이언트 팀(시스템의 출력을 사용할 팀) 과 함께 검토하는 것이

좋습니다. 만약 그 팀에서 $500,000를 넘어가더라도 정확한 예측값이 필요하다고 한다면 우리가 선택할 수 있는 방법은 두 가지입니다.


* 한곗값 밖의 구역에 대한 정확한 레이블을 구합니다.

* 훈련 세트에서 이런 구역을 제거합니다. ($500,000가 넘는 값에 대한 예측은 평가 결과가 매우 나쁠 것이므로 테스트 세트에서도 제거합니다.)


3. 특성들의 스케일이 서로 많이 다릅니다. 특성 스케일링에 대해서는 조금 있다가 살펴봅니다.


4. 마지막으로 많은 히스토그램의 꼬리가 두껍습니다. 

   가운데에서 왼쪽보다 오른쪽으로 더 멀리 뻗어 있습니다. 

   이런 형태는 일부 머신러닝 알고리즘에서 패턴을 찾기 어렵게 만듭니다. 

    나중에 이런 특성들을 좀 더 종 모양의 분포가 되도록 변형시키겠습니다. 


주의) 데이터를 깊게 들여다보기 전에, 테스트 세트를 떼어놓아야 합니다. 

      그리고 테스트 세트를 절대 들여다보면 안된다고 저자는 말합니다

      그 이유는 우리가 테스트 세트를 보면서 겉으로 드러난 어떤 패턴에 속아 특정 머신러닝 모델을 

      선택하게 될 지도 모르기 때문이라고 하네요.

      이 테스트 세트로 일반화 오차를 추정하면 매우 낙관적인 추정이 되고, 

      시스템을 론칭했을 때 기대한 성능이 나오지 않습니다.

      이를 데이터 스누핑 편향(data snooping biased) 이라고 합니다. 


이제 테스트 세트를 만들어봅니다. 무작위로 데이터를 선택해 20%를 떼어놓으면 됩니다.


** 저도 R이외엔 파이썬을 많이 다뤄보진 않았기 때문에 다뤄보면서 문법도 익혀나가려고 해요 ㅎㅎ

**일단 저 위의 코드를 읽기 위해서 Python IDLE환경에서도 한줄 한줄 해보시는 것을 추천합니다.

**reference documentation도 찾아보시는 걸 추천.


(아래는 파이썬 쉘에서 해보실 경우 임포트 예시)

housing = pd.read_csv(r'C:\Users\GOODBOY\Desktop\housing.csv')


정말 API와 다르게 검색이 되는 점이 너무 좋아용ㅋ

shuffled_indices는 보면 랜덤하게 섞는 것 같군요

test_set_size는 int(len(data)*test_ratio) 즉 data에 housing을 넣을테니 housing 데이터길이 * 0.2

test_indices 와 train_indices는 shuffled_indice의 무작위 데이터를 20% : 80%로 분류합니다.

iloc은 pandas DataFrame의 인덱싱에 사용합니다.

iloc은 integer position을 통해서 indexing합니다.

이외에 loc(lable을 통해 indexing) ix(integer position과 lable 모두 사용할 수 있다. 만약 lable이 숫자라면 lable-based indexing)

여기서는 정수 기반 위치를 사용하여 iloc을 사용하였습니다.


이후 train_set과 test_set으로 구분하여 넣어 리턴이 16512 train set과 4128 test set으로 분류된 것을 볼 수 있습니다.

이 과정을 계속하면 우리는 결국 무작위를 반복하여 전체 데이터셋을 보게 되는 셈이므로 여러 번 반복하는 일은 없어야 좋습니다.


 

이를 위한 해결책은 여러 개가 있을 수 있는데, 테스트 세트를 저장하고 다음번 실행에서 이를 불러들이는 것입니다.

또 다른 방법은 항상 같은 난수 인덱스가 생성되도록 np.random.permutation()을 호출하기 전에 np.random.seed()를 통해 난수 발생기의

초깃값을 지정하는 방법이 있습니다.


하지만 이 두 해법 모두 다음번에 업데이트된 데이터 셋을 사용하려면 문제가 됩니다. 일반적인 해결책은 샘플의 식별자를 사용하여 테스트 세트로 보낼지 말지 정하는 것입니다. (샘플이 고유하고 변경 불가능한 식별자를 가지고 있다고 가정합니다.) 예를 들어 각 샘플마다 식별자의 해시값을 계산하여 해시의 마지막 바이트 값이 51(256의 20%정도)보다 작거나 같은 샘플만 테스트 세트로 보낼 수 있습니다. 


이렇게 하면 여러 번 반복 실행되면서 데이터셋이 갱신되더라도 테스트 세트가 동일하게 유지됩니다. 새로운 테스트 세트는 새 샘플의 20%를 갖게 되지만 이전 훈련 세트에 있던 샘플을 포함시키지 않을 것입니다. 이를 구현해보는 코드는 다음과 같습니다.


######

행의 인덱스를 고유 식별자로 사용할 때 새 데이터는 데이터셋의 끝에 추가됩니다. 

이것이 불가능할 때에는 고유 식별자를 만드는 데 안전한 특성을 사용합니다. 예를들어 index나, 구역의 위도 및 경도는

몇 백년 후까지 안정적이라고 보장할 수 있으므로 다음과 같이 두 값을 연결하여 다음과 같이 ID를 만들 수 있습니다.


from zlib import crc32


def test_set_check(identifier, test_ratio):

    return crc32(np.int64(identifier)) & 0xffffffff < test_ratio *2**32


def split_train_test_by_id(data, test_ratio, id_column):

    ids=data[id_column]

    in_test_set = ids.apply(lambda id_: test_set_check(id_,test_ratio))

    return data.loc(~in_test_set), data.loc[in_test_set]


housing_with_id = housing.reset_index()

train_set, test_set=split_train_test_by_id(housing_with_id, 0.2, "index")


housing_with_id["id"] = housing["longitude"] * 1000 + housing["latitude"]

train_set, test_set =split_train_test_by_id(housing_with_id, 0.2, "id")


현재 에러가 나서 원인을 알아보는 중입니다.

Series objects are mutable, thus they cannot be hashed.

시리즈 오브젝트는 변할 수 있다. 그래서 그들은 해쉬될 수 없다.라네용.


그리고, 위치정보는 사실 정밀도가 낮아 여러 구역의 ID가 동일해지므로 원치 않은 샘플링 현상을 초래할 수도 있다고 합니다.


#####


일단 다음으로 넘어가서, 사이킷런은 데이터셋을 여러 서브셋으로 나누는 다양한 방법을 제공합니다.

가장 간단한 함수 : train_test_split

특징 : 난수 초깃값을 지정할 수 있는 random_state 매개변수가 있고, 행의 개수가 같은 여러 개의 데이터셋을 넘겨서 같은 인덱스를 기반으로

           나눌 수 있다. (이는 데이터프레임이 레이블에 따라 여러 개로 나뉘어 있을 때 유용하다.)


지금까지는 순수하게 무작위 샘플링 방식을 했습니다. 데이터 셋이 충분히 크다면 (특히 특성 수에 비해) 괜찮지만, 샘플링 편향이 생길 가능성이 큽니다.

이전의 선거에 대한 전화, 우편조사를 통해 편향되었던 사실이 있었죠. (미국)


한번 쯤 읽어보셔도 좋은 내용입니다.. 샘플링 편향에 대한 대표적인 일화..!


1936년 미국 대선 때 인기 잡지 ‘리터러리 다이제스트’는 무려 1000만명에게 발송한 우편엽서 조사로 랜든의 승리를 예측했다. 한데 뚜껑을 열어보니 루스벨트의 압승이었다. 망신살이 뻗친 리터러리는 2년 뒤 폐간했다. 반면 신생업체 갤럽은 불과 1500명을 면접조사해 결과를 정확히 맞혔다. 1948년 대선에선 갤럽이 낭패를 봤다. 듀이 50%, 트루먼 44%로 예측했는데 결과는 정반대였다. 덩달아 성급하게 듀이가 이겼다고 보도한 ‘시카고 데일리 트리뷴’은 세계적인 오보를 날렸다.

오류 원인은 표본 추출(표집)에 있었다. 리터러리의 조사대상은 구독자, 자동차 소유자, 전화가입자 등 중상층에 국한돼 샘플링 편향을 초래했다. 갤럽이 틀린 것은 모든 유권자가 표본에 선정될 확률이 동일한 확률표집이 아니라 지역·성별·연령별로 미리 할당된 숫자만 채운 비확률표집에 의존한 탓이었다. 


​만약 한국인 인구 분포가 남자 52% 여자 48%라면, 1000명의 샘플링에 있어 남성 520, 여자 480으로 맞추는 것이 계층적 샘플링이라고 합니다. 

전체 모수는 계층(Strata)이라는 동질의 그룹으로 나뉘고, 테스트 세트가 전체 모수를 대표하도록 각 계층에서 올바른 수의 샘플을 추출합니다.


이제, 전문가가 중간 소득이 중간 주택 가격을 예측하는 데 매우 중요하다고 말했다고 가정했다고 합니다.

이 경우, 테스트 세트가 전체 데이터셋에 있는 여러 소득 카테고리를 잘 대표해야 합니다. 중간 소득이 연속적인 숫자형 특성이므로, 

소득에 대한 카테고리를 만들어야 합니다. 중간 소득의 히스토그램을 조금 더 자세히 살펴보겠습니다.

중간 소득 대부분은 $20,000~$50,000 사이에 모여 있지만 일부는 $60,000을 넘기도 합니다.


test_size를 0.2로 설정한 후, 

housing에 income_cat이라는 열을 하나 만듭니다. 

중간 소득을 1.5로 나누는 이유는 소득의 카테고리 수가 너무 많아지지 않기 위해 (제한하기 위해) 나누어주고, 

numpy.ceil을 써서 반올림하여 소득 카테고리 특성을 만든 것입니다.

이산적인 카테고리 특성을 위해, 5보다 큰 카테고리 특성은 5로 합칩니다.




StratifiedShuffleSplit은 StratifiedKFold의 계층 샘플링과 ShuffleSplit의 랜덤 샘플링을 합친 것으로, 

test_size와 train_size의 합이 1 이하로도

지정할 수 있습니다.


이제 income_cat 특성을 삭제해서 원래 상태로 데이터를 되돌립니다.


for set_  in (strat_train_set, strat_test_set):

    set_.drop("income_cat", axis=1, inplace=True)


머신러닝 프로젝트에 있어서 테스트 세트 생성은 중요한 부분입니다. 게다가 이런 아이디어들은 나중에 

교차 검증에 대해 이야기할 때 도움이 된다고 하네요!

다음 차에서는 데이터 이해를 위한 탐색과 시각화를 해보죵..


 블로그 

출처


이 글의 상당 부분은  [핸즈온 머신러닝, 한빛미디어/오렐리앙 제롱/박해선] 서적을 참고하였습니다.


나머지는 부수적인 함수나 메서드에 대해 부족한 설명을 적어두었습니다.

학습용으로 포스팅 하는 것이기 때문에 복제보다는 머신러닝에 관심이 있다면 구매해보시길 추천합니다.


도움이 되셨다면 로그인 없이 가능한

아래 하트♥공감 버튼을 꾹 눌러주세요! 



반응형