https://school.programmers.co.kr/learn/courses/30/lessons/258712

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

처음에는 딕셔너리를 만들어 선물을 준 사람과 받은 사람관의 관계를 나타내고, 각 사람의 [준 선물/ 받은 선물 / 선물 지수]를 저장할 딕셔너리를 생성했다.

이후 서로간의 관계에 따라 다음달에 받을 선물을 확인하는 로직을 작성하던 중, 내가 받았을 때와 받지 않았을 때의 관계를 찾는것이 너무 어려웠다.

def solution(friends, gifts):
    answer = {x:0 for x in friends}
    gifts_to_friends = {x:{} for x in friends}
    gifts_count = {x:[0,0,0] for x in friends}
    for i in gifts:
        give, take = i.split()
        gifts_count[take][1] += 1 # 받은 선물 카운팅
        if take in gifts_to_friends[give]:
            gifts_to_friends[give][take] += 1 # 선물 전달 내역 기록
            gifts_count[give][0] += 1 # 준 선물 카운팅
        else:
            gifts_to_friends[give][take] = 1
            gifts_count[give][0] += 1
            
    for name in gifts_count:  #선물 지수 구하기
        gifts_count[name][2] = gifts_count[name][0] - gifts_count[name][1]
            
    print(gifts_to_friends)
    print(gifts_count)
    
    for giving_name in gifts_to_friends:
        for friend in friends:
            if giving_name != friend: #자신인 경우 제외
                if gifts_to_friends[giving_name]: # 내가 선물을 준 사람이 있다면 
                    for taking_name in gifts_to_friends[giving_name]: #여기까지는 ok
                        if giving_name in gifts_to_friends[taking_name]: #상대도 나에게 주었다면
                            if gifts_to_friends[giving_name][taking_name] > gifts_to_friends[taking_name][giving_name]:
                                answer[giving_name]+=1
                            elif gifts_to_friends[giving_name][taking_name] == gifts_to_friends[taking_name][giving_name]:
                                if gifts_count[giving_name][2] > gifts_count[taking_name][2]:
                                    answer[giving_name]+=1
                    
    
    return max(answer.values())

 

나는 자신인 경우를 제외하였지만, 또 다시 어떤 관계에 대해 계산하는 문제가 있었다.

이 문제는 enumerate를 활용해 index 슬라이싱을 활용해 해결하였다.

 

관계가 없는 상황을 계산하는 것이 어려웠다. 이 문제는 딕셔너리 메서드 중 .get()메서드를 통해 해결했다.

.get() 메서드는 키를 지정해주고 그 키에대한 값이 있다면 반환해주고, 없다면 특정 값을 지정해 반환할 수 있다.

def solution(friends, gifts):
    answer = {x:0 for x in friends}
    gifts_to_friends = {x:{} for x in friends}
    gifts_count = {x:[0,0,0] for x in friends}
    for i in gifts:
        give, take = i.split()
        gifts_count[take][1] += 1 # 받은 선물 카운팅
        if take in gifts_to_friends[give]:
            gifts_to_friends[give][take] += 1 # 선물 전달 내역 기록
        else:
            gifts_to_friends[give][take] = 1
        gifts_count[give][0] += 1 # 준 선물 카운팅
            
    for name in gifts_count:  #선물 지수 구하기
        gifts_count[name][2] = gifts_count[name][0] - gifts_count[name][1]
            
    # print(gifts_to_friends)
    # print(gifts_count)
    
    for i, giving_name in enumerate(friends):
        for j, taking_name in enumerate(friends[i+1:], i+1): # 나를 제외 / 이전에 확인한 것은 제외
            if giving_name != taking_name:
                give_count = gifts_to_friends[giving_name].get(taking_name, 0) # .get()메서드 / 딕셔너리에서 해당 키의 값이 있다면 반환, 없다면 반환할 값을 지정 가능
                take_count = gifts_to_friends[taking_name].get(giving_name, 0)
                
                if give_count > take_count:
                    answer[giving_name] += 1
                elif give_count < take_count:
                    answer[taking_name] += 1
                else:  # 선물을 주고받은 기록이 없거나 수가 같은 경우
                    if gifts_count[giving_name][2] > gifts_count[taking_name][2]:
                        answer[giving_name] += 1
                    elif gifts_count[giving_name][2] < gifts_count[taking_name][2]:
                        answer[taking_name] += 1
                    
    
    return max(answer.values())

이렇게 해당 문제를 풀이했다.

https://school.programmers.co.kr/learn/courses/30/lessons/340212

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

이 문제는 간단한 수식을 통해 해결할 수 있을거라 생각했다.

def solution(diffs, times, limit):
    time_prev = [(times[x-1]+times[x]) for x in range(1, len(times))]
    time_prev = [times[0]] + time_prev
    # print(time_prev)
    time = 10**15 + 1
    level = 0
    while (time > limit):
        time = 0
        level += 1
        for i in range(len(diffs)):
            if time > limit:
                break
                
            if diffs[i] <= level:
                time += times[i]
                # print('if문')
                # print(level, time)
            else:
                time += time_prev[i] * (diffs[i] - level) + times[i]
                # print('level :', level)
                # print("else문 itme :", time)
        
        
    return level

다음과 같이 수식을 활용해 풀었으나 시간 초과 오류가 발생했다.

수가 커지면서 너무 많은 반복을 해서 그런거 같다.

반복 횟수를 줄여야 한다는 생각을 하였고 이진 탐색을 통해 횟수를 줄일 수 있다고 생각했다.

 

Left, Right를 지정해주고 가운데 지점부터 확인하면서 Left, Right를 초기화 해준다.

def solution(diffs, times, limit):
    time_prev = [(times[x-1]+times[x]) for x in range(1, len(times))] # time_prev를 미리 계산
    time_prev = [times[0]] + time_prev # 첫 번째값 대입

    left, right = 1, max(diffs) # 이진 탐색을 위한 index 지정
    while left <= right: # lift > right라면 그 지점이 최초로 time <= limit인 부분
        level = (left + right) // 2 # 중간 지점 확인(정수형)
        time = 0
        for i in range(len(diffs)):
            if time > limit: # 시간초과 발생시 다음 탐색
                break
            
            time += times[i] # 기본적으로 times[i]를 더해주기 때문에 지정 
            if diffs[i] > level: # 숙련도가 낮을 경우 시간 추가
                time += (diffs[i] - level) * time_prev[i]

        if time <= limit: # 시간이 리미트를 넘어서지 않은 경우 오른쪽 지점을 변경
            right = level - 1
        else:
            left = level + 1
    
    return left

 

MySQL은 다음과 같이 설치해 주면 된다.

꼭 버전을 확인하고 알맞은 버전으로 설치해야 된다.

 

커뮤니티를 설치하고 새로운 DB를 사용하기 위해서는 schema칸을 선택하고 빈 공간에 우클릭후 BD이름을 작성한다.

하단의 Apply를 누르면 새로운 DB를 만들 수 있다. 생성된 DB를 더블클릭하면 해당 DB가 선택되고 진한 글씨로 바뀐다.

선택된 DB에서 쿼리문이 돌아가게 된다.

 

 

테이블을 생성하고 항목의 데이터를 보고싶다면, 해당 테이블을 우클릭하여 Select Rows를 실행하면 자동으로 확인할 수 있는 쿼리문을 작성해준다. 상단을 보면 Limit를 걸어둔 것을 볼 수 있다.

실문에서는 정말 많은 데이터를 갖고있기 때문에 리미트가 없다면 큰일이 날 수 있다.

우리가 다루는 데이터는 적은량의 데이터기에 리미트를 걸지 않아도 되지만 실무에서는 꼭 확인하고 리미트를 걸도록 하자.

 

 

SQL 문

SQL: Structured Query Language
•  데이터베이스에서데이터를 조회하거나처리(입력,수정,삭제)할 때사용하는 구문

 

주석

범위 주석 : /* */

한줄 주석 : -- 

https://school.programmers.co.kr/learn/courses/30/lessons/43105

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

처음에 모든뿌리를 따라 depth를 만들고 그 안에 맞는 값을 넣은 후, 최대값을 찾아내는 방식을 생각했다가 안 된다는걸 확인했다.

이후 트리구조를 따라가면서 더해지는 값을 비교하고 더 큰 값을 넣어주는 방식을 선택했다.

 

def solution(triangle):
    answer = 0
    depth = len(triangle)
    result = {}
    i = 0
    while(i < depth):
        result['depth'+str(i+1)] = []
        for index, n in enumerate(triangle[i]):
            try:
                if index == 0:
                    result['depth'+str(i+1)].append(result['depth'+str(i)][index]+n)
                elif index == (len(triangle[i]) - 1):
                    result['depth'+str(i+1)].append(result['depth'+str(i)][index-1]+n)
                else:
                    result1 = result['depth'+str(i)][index-1]+n
                    result2 = result['depth'+str(i)][index]+n
                    if (result1 >= result2):
                        result['depth'+str(i+1)].append(result1)
                    elif (result1 < result2):
                        result['depth'+str(i+1)].append(result2)
            except:
                if i+1 == 1:
                    result['depth'+str(i+1)].append(n)
                else:
                    continue
        i+=1
        
    answer = max(result['depth'+str(depth)])
    # print(result)
    return answer

 

이렇게 풀이하였고 통과했다.

풀고나니 꼭 딕셔너리를 사용했어야 됐나? 생각이 많이 들어 리스트를 쓰면 좀 더 단순화된 코드가 나올 것 같다.

https://school.programmers.co.kr/learn/courses/30/lessons/132265?language=python3

 

프로그래머스

SW개발자를 위한 평가, 교육, 채용까지 Total Solution을 제공하는 개발자 성장을 위한 베이스캠프

programmers.co.kr

 

이 문제를 처음 접했을 때는 인덱스 슬라이싱을 먼저 떠올렸다.

그래서 for 문을 돌리며 인덱스를 기준으로 left와 right를 자르고 len(), set()을 통하여 포함되어 있는 토핑 갯수를 확인하였다.

그러나 시간초과 문제를 직면하였다.

 

그 다음에는 deque를 활용해서 큐 자료구조를 활용하였다.

최대한 연산을 줄이고 길이를 나타내는 변수(check, count)를 만들어서 비교하였다.

절반만 맞고, 절반은 시간초과가 떴다.

from collections import deque

def solution(topping):
    answer = 0
    check = len(set(topping))
    count = 0
    dq1 = deque()
    dq2 = deque(topping)

    for i in topping:
        dq2.popleft()
        if (not i in dq1):
            dq1.append(i)
            count += 1

        if (not i in dq2):
            check -= 1
        
        if (count < check):
            continue
        elif (count > check):
            break
        else:
            answer += 1
    return answer
    
topping = [1, 2, 1, 3, 1, 4, 1, 2]

print(solution(topping))

 

가장 마지막 방식으로는 딕셔너리를 활요해 문제를 풀었다.

확실히 딕셔너리로 풀었을 때 가장 시간이 단축되었다.

문제의 형태에 따라 좋은 방식을 선택한다면 풀이하는 시간을 많이 줄일 수 있다.

주석 부분은 처음에는 사용했지만, 없어도 문제가 풀리기 때문에 주석처리 했다.

처음에 이 코드를 이해하려면 필요하기에 남겨두었다.

def solution(topping):
    answer = 0
    left_dic = {}
    right_dic = {}
    left_len = 0
    right_len = 0

    for d in set(topping):
        right_dic[d] = 0
        right_len += 1
    
    for i in topping:
        right_dic[i] += 1
        
    for j in topping:
        
        if (not j in left_dic):
            left_dic[j] = 0
            left_len += 1
        
        # left_dic[j] += 1
        right_dic[j] -= 1
        
        if right_dic[j] == 0:
            # del right_dic[j]
            right_len -= 1
            
        if (left_len < right_len):
            continue
        elif (left_len > right_len):
            break
        else:
            answer+=1
    
    return answer
    
topping = [1, 2, 1, 3, 1, 4, 1, 2]

print(solution(topping))

https://school.programmers.co.kr/learn/courses/30/lessons/150368

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

처음 이 문제를 풀이할 때 전체 할인률을 [10, 20, 30, 40]으로 각각 지정하여 플러스 이용자가 가장 많은 할인률을 확인한 뒤, 그 범위 안에서 좁혀 나갈 생각을 하였다.

그러나 문제를 풀이하면서 전체의 결과를 다 확인해야 된다는 것을 확인하였다.

그래서 모든 할인률을 확인하기 위해 중복 순열을 사용하였다.

from itertools import product 

def solution(users, emoticons):
    # users : n명의 구매 기준을 담은 2차원 정수 배열 [비율, 가격]
    # emoticons :m개의 정가를 담은 1차원 정수 배열
    for i in users:
        if i[0]%10 != 0:
            i[0] = ((i[0] // 10) + 1)*10 

    sales = list(product([10, 20, 30, 40], repeat = len(emoticons)))
    
    best_param = [0,0,0] #가장 값이 좋은 plus_user, sum_cost, sales값
    for sale in sales:
        plus_user = 0
        sum_cost = 0
       
        for user in users:
            cost = 0
            for j in range(len(emoticons)):
                if user[0] <= sale[j]:
                    cost += (emoticons[j] * (1-(sale[j] / 100)) )

            if user[1] <= cost:
                plus_user += 1
                cost = 0
            sum_cost += cost
        
        if (best_param[0] < plus_user):
            best_param[0] = plus_user
            best_param[1] = sum_cost
            best_param[2] = sale
        elif(best_param[0] == plus_user):
            if (best_param[1] <= sum_cost):
                best_param[1] = sum_cost
                best_param[2] = sale
    
    answer = [best_param[0], int(best_param[1])]

    return answer

 

돌아가는 원리를 파악하고 싶다면 아래의 코드를 실행해 보기를 추천한다.

단 원리를 파악할 때 먼저 2명의 users를 확인하길 바란다.

from itertools import product 

def solution(users, emoticons):
    # users : n명의 구매 기준을 담은 2차원 정수 배열 [비율, 가격]
    # emoticons :m개의 정가를 담은 1차원 정수 배열
    for i in users:
        if i[0]%10 != 0:
            i[0] = ((i[0] // 10) + 1)*10 

    sales = list(product([10, 20, 30, 40], repeat = len(emoticons)))
    print(sales)
    
    best_param = [0,0,0] #가장 값이 좋은 plus_user, sum_cost, sales값
    for sale in sales:
        plus_user = 0
        sum_cost = 0
        print('sale =',sale)
        for user in users:
            cost = 0
            for j in range(len(emoticons)):
                if user[0] <= sale[j]:
                    cost += (emoticons[j] * (1-(sale[j] / 100)) )

            print('cost =', cost)
            if user[1] <= cost:
                plus_user += 1
                cost = 0
            sum_cost += cost
        
        if (best_param[0] < plus_user):
            best_param[0] = plus_user
            best_param[1] = sum_cost
            best_param[2] = sale
        elif(best_param[0] == plus_user):
            if (best_param[1] <= sum_cost):
                best_param[1] = sum_cost
                best_param[2] = sale

        print('플러스 이용자 =',plus_user, '최종금액 =',sum_cost)
        print('result =', best_param[0], best_param[1])
        print('='*30)
    
    answer = [best_param[0], int(best_param[1])]
    print('best_param =', best_param[2])
    
    
    return answer

# users = [[40, 2900], [23, 10000], [11, 5200], [5, 5900], [40, 3100], [27, 9200], [32, 6900]]
# emoticons = [1300, 1500, 1600, 4900]

users = [[40, 10000], [25, 10000]]
emoticons = [7000, 9000]
print(solution(users, emoticons))

https://school.programmers.co.kr/learn/courses/30/lessons/43165

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

처음 코드는 다음과 같다.

개인적은 dfs(깊이 우선 탐색) 보다 bfs(너비 우선 탐색)의 방식이 나한테 잘 맞는다고 생각했다.

그러나 이 코드에서는 시간 초과가 떴는데, 문제점을 살펴본 결과 pop(0)의 과정에서 n개의 모든 원소를 확인하여 빼기때문에 시간 복잡도는 O(n)이라고 한다.

만약 depue의 자료형이라면 popleft()을 활용해 O(1)의 시간복잡도로 문제를 풀이할 수 있다.

def solution(numbers, target):
    answer = 0
    q = []
    n = len(numbers)
    q.append([numbers[0],0])
    q.append([-(numbers[0]), 0])
    while q:
        num, index = q.pop(0)
        index += 1
        if index != n:
            q.append([num + numbers[index], index])
            q.append([num - numbers[index], index])
        else:
            if num == target:
                answer += 1
    return answer

 

from collections import deque를 선언하고

popleft() 함수를 활용해 다음과 같이 수정했다.

from collections import deque
def solution(numbers, target):
    answer = 0
    q = deque()
    n = len(numbers)
    q.append([numbers[0],0])
    q.append([-(numbers[0]), 0])
    while q:
        num, index = q.popleft()
        index += 1
        if index != n:
            q.append([num + numbers[index], index])
            q.append([num - numbers[index], index])
        else:
            if num == target:
                answer += 1
    return answer

Sequential

  • 순차적으로 쌓아가며 모델 생성
  • Input → Output Layer로 순차적 연결
  • Sequential 함수 안에 리스트로 레이어 입력
model = Sequential([Input(shape = (n, )),
                    Dense(128, activation = 'relu'),
                    Dense(64, activation = 'relu'),
                    Dense(32, activation = 'relu'),
                    Dense(16, activation = 'relu'),
                    Dense(10, activation = 'softmax')])

 

Function

  • 모델을 좀더 복잡하게 구성
  • 모델을 분리해서 사용 가능
  • 다중 입력, 다중 출력 가능
  • 레이어 : 앞 레이어 연결 지정
  • Model 함수로 시작과 끝 연결해서 선언
il = Input(shape = (nfeatures, ))
hl1 = Dense(10, activation = 'relu')(il)
hl2 = Dense(10, activation = 'relu')(hl1)
hl3 = Dense(2, activation = 'relu')(hl2)
ol = Dense(1)(hl3)

model = Model(inputs = il, outputs = ol)

 

 

다중 입력

모델 선언

  • concatenate 
  • 옆으로 붙이기, 하나의 레이어처럼 묶기
  • cbl = concatenate([hl1_1, hl1_2])

모델 사용

  • 모델 예측 시, 전처리 된 두 가지 입력을 리스트로 묶어서 사용
  • pred = model.predict([x_val1, x_val2]) 
# 모델 구성
input_1 = Input(shape=(nfeatures1,), name='input_1')
input_2 = Input(shape=(nfeatures2,), name='input_2')

# 첫 번째 입력을 위한 레이어
hl1_1 = Dense(10, activation='relu')(input_1)

# 두 번째 입력을 위한 레이어
hl1_2 = Dense(20, activation='relu')(input_2)

# 두 히든레이어 결합
cbl = concatenate([hl1_1, hl1_2])

# 추가 히든레이어
hl2 = Dense(8, activation='relu')(cbl)

# 출력 레이어
output = Dense(1)(hl2)

# 모델 선언
model = Model(inputs = [input_1, input_2], outputs = output)

model.summary()

 

시계열 데이터

ML 기반 시계열 모델링

  • 특정 시점 데이터들(1차원)과 예측대상시점(𝑦𝑡+1) 과의 관계로 부터 패턴을 추출하여 예측
  • 모델 구조 예 : 𝑦𝑡+1 = 𝑤1𝑥1𝑡 + 𝑤2𝑥2𝑡+ 𝑤3𝑥3𝑡 + 𝑤4𝑦𝑡 + 𝑤0
  • 시간의 흐름을 x변수로 도출하는 것이 중요.

DL 기반 시계열 모델링

  • 시간흐름 구간(timesteps) 데이터들(2차원)과 예측대상시점(𝑦𝑡+1) 과의 관계로 부터 패턴 추출
  • 어느정도 구간(timesteps)을 하나의 단위로 정할 것인가?
  • 분석 단위를 2차원으로 만드는 전처리 필요. ➔ 데이터셋은 3차원

 

잔차 분석

잔차(Residuals) = 실제 데이터 – 예측값

  • 시계열 모델 𝑦 = 𝑓(𝑥) + 𝜀
  • 모델이 잘 만들어 졌다면, 잔차𝜀는 White Noise에 가까워야 함.
  • 잔차𝜀가 White Noise에 가깝지 않다면 𝑓(𝑥)는 아직 𝑦 의 패턴을 제대로 반영하지 않음. ➔ 더 해야 할 일이 남아 있음.

잔차 분석

  • 시각화 : ACF, PACF
  • 검정
    • 정상성 검정(ADF Test, KPSS Test)
    • 정규성 검정(Shapiro-wilk Test)
    • 자기상관 검정(Ljung-Box Test)
    • 등분산성 검정(G-Q Test)

 

RNN(Recurrent Neural Networks)

 

RNN으로 시계열 데이터 모델링 하기

✓과거의 정보를 현재에 반영해 학습하도록 설계

 

RNN을 위한 데이터 전처리

  • 데이터 분할 1 : x, y
  • 스케일링
    • X 스케일링은 필수
    • Y 값이 크다면 최적화를 위해 스케일링 필요 ➔ 단, 모델 평가 시 원래 값으로 복원
  •  3차원 데이터셋 만들기 
  • 데이터 분할2 : train, val

 

SimpleRNN

  • 노드 수 1개 ➔ 레이어의 출력 형태 : timesteps * 노드 수
  • return_sequences : 출력 데이터를 다음 레이어에 전달할 크기 결정 ★ ★
    • True : 출력 크기 그대로 전달 ➔ timesteps * node수
    • False : 가장 마지막(최근) hidden state 값만 전달 ➔ 1 * node 수
    • 마지막 RNN Layer 를 제외한 모든 RNN Layer : True
    • 마지막 RNN Layer : False와 True 모두 사용 가능
    • 단, True를 사용하려면 Flatten으로 펼친 후 Dense Layer 로 연결

'<프로그래밍> > [python]' 카테고리의 다른 글

[python] 딥러닝 성능관리  (0) 2024.10.14
[python] 딥러닝 기초  (0) 2024.10.10
[python] 머신러닝의 기초3  (0) 2024.09.30
[python] 머신러닝의 기초2  (1) 2024.09.27
[python] 머신러닝의 기초  (0) 2024.09.26

모델의 복잡도와 과적합

모델의 복잡도

  • 너무 단순한 모델 : train, val 성능이 떨어짐
  • 적절히 복잡한 모델 : 적절한 예측력
  • 너무 복잡한 모델 : train 성능 높고, val 성능 떨어짐

과적합을 해결하기 위해서 여러 요소를 조절해야 됨

  • Epoch와 learning_rate
  • 모델 구조 : hidden layer 수, node 수
  • 미리 멈춤 Early Stopping
  • 임의 연결 끊기 Dropout
  • 가중치 규제하기 Regularization(L1, L2)

 

Early Stopping

반복 횟수(epoch)가 많으면 과적합 될 수 있음

  • 항상 과적합이 발생되는 것 X
  • 반복횟수가 증가할 수록 val error가 줄어들다가 어느 순간부터 다시 증가할 수 있음.
  • val error가 더 이상 줄지 않으면 멈춰라 ➔ Early Stopping
  • 일반적으로 train error는 계속 줄어듦

EarlyStopping 옵션

  • monitor : 기본값 val_loss
  • min_delta : 오차(loss)의 최소값에서 변화량(줄어드는 량)이 몇 이상 되어야 하는지 지정. (기본값 0)
  • patience : 오차가 줄어들지 않는 상황을 몇 번(epoch) 기다려줄 건지 지정. (기본값 0) ✓
    • from keras.callbacks import EarlyStopping
    • es = EarlyStopping(monitor = 'val_loss', min_delta = 0, patience = 0)
  • fit 안에 지정
  •  callbacks : epoch 단위로 학습이 진행되는 동안, 중간에 개입할 task 지정 
    • model.fit(x_train, y_train, epochs = 100, validation_split = .2, callbacks = [es])
from keras.callbacks import EarlyStopping

# 모델 선언
clear_session()

model2 = Sequential( [Input(shape = (nfeatures,)),
                      Dense(128, activation= 'relu'),
                      Dense(64, activation= 'relu'),
                      Dense(32, activation= 'relu'),
                      Dense(1, activation= 'sigmoid')] )
model2.compile(optimizer= Adam(learning_rate = 0.001), loss='binary_crossentropy')

# EarlyStopping 설정 ------------
min_de = 0.0005
pat = 7

es = EarlyStopping(monitor = 'val_loss', min_delta = min_de, patience = pat)

# 학습
hist = model2.fit(x_train, y_train, epochs = 100, validation_split=0.2,
                  callbacks = [es]).history

Dropout - 연결을 임의로 끊기 

Dropout

  • 과적합을 줄이기 위해 사용되는 규제(regularization) 기법 중 하나
  • 학습시, 신경망의 일부 뉴런을 임의로 비활성화 ➔ 모델을 강제로 일반화

학습 시 적용 절차

  • 훈련 배치에서 랜덤하게 선택된 일부 뉴런을 제거
  • 제거된 뉴런은 해당 배치에 대한 순전파 및 역전파 과정에서 비활성화
  • 이를 통해 뉴런들 간의 복잡한 의존성을 줄여 줌
  • 매 epochs 마다 다른 부분 집합의 뉴런을 비활성화 ➔ 앙상블 효과

 

Dropout Rate 옵션

  • Hidden Layer 다음에 Dropout Layer 추가
  • Import : from keras.layers import Dropout
  • ex) Dropout(0.4) : hidden layer의 노드 중 40%를 임의로 제외시킴. 
  • 보통 0.2 ~ 0.5 사이의 범위 지정
  • 조절하면서 찾아야 하는 하이퍼파라미터!
  • Feature가 적을 경우 rate를 낮추고, 많을 경우는 rate를 높이는 시도
from keras.layers import Dropout

# input_shape : feature 수 도출
nfeatures = x_train.shape[1]

# 메모리 정리
clear_session()

# Sequential 타입
model3 = Sequential( [Input(shape = (nfeatures,)),
                      Dense(128, activation= 'relu'),
                      Dropout(0.4),
                      Dense(64, activation= 'relu'),
                      Dropout(0.4),
                      Dense(32, activation= 'relu'),
                      Dropout(0.4),
                      Dense(1, activation= 'sigmoid')] )

# 컴파일
model3.compile(optimizer= Adam(learning_rate = 0.001), loss='binary_crossentropy')

# 학습
hist = model3.fit(x_train, y_train, epochs = 50, validation_split=0.2, verbose = 0).history

성능관리 요약

데이터

  • 입력 데이터 정제, 적절한 전처리
  • 데이터 늘리기 : 열(적절한 feature 추가), 행(데이터 건수 늘리기) 

모델 구조

  • 은닉층, 노드 수 늘리기 : 성능이 증가할 때 까지 
  • activation 

학습

  • epochs, learning_rate, optimizer  

과적합 문제

  • 모델링 목적 : 모집단 전체에서 두루 잘 맞추는 (적당한) 모델 만들기
  • 과적합 : 학습 데이터에서만 높은 성능, 다른 데이터에서는 낮은 성능

과적합 문제 해결

  • 데이터 건수 늘리기
  • 모델 복잡도 조절하기 ➔ 가중치 규제(Regularization)
  • 반복 학습 횟수(epochs) 적당히 ➔ early stopping

모델 저장하기

  • 최종 모델 저장
  • 체크포인트에서 모델 저장
  • 성능이 개선되면 저장하기 가능.

lambda는 함수를 딱 한줄로 만들수 있게 해준다.

기본적인 표현식은 다음과 같다.

 

lambda 매개변수 : 표현

 

map(함수, 리스트)

map함수는 함수와 리스트를 인자로 받는다.

리스트로부터 원소를 하나씩 꺼내 함수를 적용시키고, 새로운 리스트에 담아준다.

이를 lambda를 활용하면 다음고 같이 활용 가능하다.

L = list(map(lambda x: x ** 2, range(5)))
print(L)
[0, 1, 4, 9, 16]

L을 출력하면 [0, 1, 4, 16] 이란 결과를 확인할 수 있다.

 

reduce(함수, 스퀸스(문자열, 리스트, 튜플))

reduce함수는 스퀸스 원소들을 누적해서 함수에 적용시킨다.

from functools import reduce   
L = reduce(lambda x, y: x + y, [0, 1, 2, 3, 4])
print(L)
10

 

문자열에서의 활용도 가능하고 x와 y를 바꿔준다면 순서가 바뀌게 된다.

from functools import reduce   
L = reduce(lambda x, y: y + x, '요세하녕안')
print(L)
안녕하세요

 

filter(함수, 리스트)

원소들을 함수에 적용시켜 참인 값만 새로운 리스트로 반환한다.

L = list(filter(lambda x: x < 5, range(10)))
print(L)
[0, 1, 2, 3, 4]

 

L = list(filter(lambda x: x % 2, range(10)))
print(L)
[1, 3, 5, 7, 9]

 

sorted(정렬할 데이터, key 파라미터, reverse 파라미터)

dict = {'a': 400, 'b':300, 'c':200, 'd':00, 'e':500}
print('원본 딕셔너리')
print(dict.items())
print('\n딕셔너리 정렬 : sorted(d.items())')
a = sorted(dict.items())
print(a)
print('\n딕셔너리 정렬 : sorted(d.items(), key=lambda x: x[1])')
b = sorted(dict.items(), key=lambda x: x[1])
print(b)
print('\n딕셔너리 정렬 : sorted(d.items(), key=lambda x: x[1], reverse=True)')
c = sorted(dict.items(), key=lambda x: x[1], reverse=True)
print(c)
원본 딕셔너리
dict_items([('a', 400), ('b', 300), ('c', 200), ('d', 0), ('e', 500)])

딕셔너리 정렬 : sorted(d.items())
[('a', 400), ('b', 300), ('c', 200), ('d', 0), ('e', 500)]

딕셔너리 정렬 : sorted(d.items(), key=lambda x: x[1])
[('d', 0), ('c', 200), ('b', 300), ('a', 400), ('e', 500)]

딕셔너리 정렬 : sorted(d.items(), key=lambda x: x[1], reverse=True)
[('e', 500), ('a', 400), ('b', 300), ('c', 200), ('d', 0)]

 

sorted 대신 sort를 활용하면 다음과 같이 쓸 수 있다.

문자의 길이대로 우선 정렬하고, 길이가 같다면 사전순으로 정렬한다.

L = ['monk', 'apply', 'angel', 'apply', 'elephant', 'encyclopedia']
L = list(set(L))
L.sort(key = lambda x: (len(x), x))

for x in L:
    print(x)
monk
angel
apply
elephant
encyclopedia

+ Recent posts