728x90
반응형

Python

#목표: 텐서플로우를 활용하여 직접 구현하는 것을 목표로 한다

명령형 프로그래밍 vs 선언형 프로그래밍

명령형 프로그래밍

프로그래밍의 상태와 상태를 변경시키는 구문의 관점에서 연산을 설명하는 프로그래밍 패러다임의 일종이다. 명령형 프로그램은 컴퓨터가 수행할 명령들을 순서대로 써 놓은 것이다.

ex) 절차지향, 객체지향 프로그래밍

 

선언형 프로그래밍

선언형 프로그래밍은 무엇인가를 작업하기 위한 방법을 정의한다.

Python에서는 선언 대신 정의라는 말을 쓰고 어떤 기능을 미리 만들어 놓고 조합하여 결과를 내는 방식을 따른다

ex) 함수형 프로그래밍

 

함수형 프로그래밍의 특징

1. 코드가 간결해진다

- 내부 구조를 몰라도 input, output만 알면 사용 가능

2. 수학적으로 증명이 가능하다

3. for, while문을 자제하고 iter 사용한다

4. 수학적 증명이 필요하기 때문에 구현이 어렵다

- , python에서는 multi paradiam이기 때문에 적절히 혼용 가능

5. 디버깅, 테스트가 용이하다

6. 모듈성, 결합성이 있다.

7. mutable은 처리하지 않는다

8. vectorization 연산을 지원한다

 

# tensorflow에서 함수형 프로그래밍은 형식적 증명가능성이라는 표현이 나온다

# 이때 형식적 증명가능성이란 이론적으로는 가능하지만 실제로는 안될 가능성도 있다는 의미 이다

# 함수형 프로그래밍은 함수 자체가 수학함수로 만들기 때문에 증명이 가능하고 이론과 구현의 간극을 줄여주기 때문에 데이터 처리에 관한 분야에 적합하다고 할 수 있다

 

┌─ Mutable # 추가, 삭제가 가능한 경우 / 특징 : 메모리 번지 안바뀜, 재할당할 필요없음

└─ Immutable # 추가, 삭제가 불가능한 경우 / 특징 : 재할당으로 메모리 번지 바뀜

Container

┌─ Homogeneous # 요소들이 서로 같은 타입인 경우

└─ Heterogeneous # 요소들이 서로 다른 타입이 가능한 경우

(요소가 1 이상인 데이터 구조)

Sequence

┌─ Indexing # 요소를 하나씩 출력

└─ Slicing # 요소를 한번에 여러개 출력

(순서가 있음)

Lookup

┌─ Mapping hash # key값과 value를 갖는 Dictinary가 갖는 특징

└─ set # 순서가 없는 고유한 원소들의 집합

(key값으로 이루어진 데이터 구조)

 

Container (container이면 전부다 iterable하다 , 순서대로 하나씩 뽑아 있는 데이터 타입)

- Homogeneous ex) ndarray

- Heterogeneous ex) list

- sequence ex) list

- non sequence ex) set

- mutable ex) list

- immutable ex) tuple

 

x = [1,2,3]
x = iter(x)
'__iter__' in dir(x) # __iter__가 있기 때문에 iterable하다는 의미이다 
# True

'__next__' in dir(x) #__next__가 있다면 iterator라는 의미이다
# True

from collections.abc import Iterable, Iterator
set(dir(Iterator)) - set(dir(Iterable)) 
# {'__next__'}

set(dir(Iterable)) - set(dir(Iterator))
# set()

Iterator

Iterator는 데이터 스트림을 표현하는 객체, next()메소드를 사용하여 다음 요소를 가져온다

Iterator를 활용하여 Lazy Evaluation 방법을 사용한다

 

Lazy Evaluation

- 계산 결과 값이 필요할 때까지 계산을 늦추는 방식이다

- next 실행하는 순간 연산이 시작되고 호출한 값만 메모리에 할당되므로 메모리를 효율적으로 사용할 있게된다

- 내부적으로 최적화 되어 있어 속도가 빠르다

- 방대한 데이터를 다룰때 효율적인 처리를 있다

 

주의 iterator는 scope를 초과하면 StopIteration 에러가 뜬다

 

모든 iterable은 iterator로 만들 수 있다

a = [1,2,3]
b = iter(a) # __next__ 가 생기고 next를 사용할 수 있게 된다 (iterators)
next(b)
# 1

for i in [1,2,3]: # for문에 들어간 iterable한 객체는 iterator로 변환후 사용된다 
    print(i) 
# 1
# 2
# 3

Lazy Evaluation

Lazy Evaluation은 계산 결과 값이 필요할 때까지 계산을 늦추는 방식이다 (next로 호출하는 순간 메모리에 올라간다) Lazy Evaluation은 속도가 느리다는 단점이 있지만 파이썬에서는 내부적으로 최적화 되어 있어 속도가 빠르다 메모리의 효율성을 위해 사용한다

a = [1,2,3]
b = iter(a) # next 호출하기 전까지 메모리에 올라가지 않는다 / next호출 시에만 실행 
b[0]  # iterator로 만들면 sequence한 성질을 잃는다 
# 'list_iterator' object is not subscriptable
a = range(10)
b = iter(a)
next(b)
# 0

list(b)
# [1, 2, 3, 4, 5, 6, 7, 8, 9]

next(b) 
# list를 통해 나머지 요소가 전부 출력 되었으므로 더 이상 남아있는 데이터가 없어서 StopIterationError가 발생한다 
# StopIteration:

 

Generator

Iterator를 생성해주는 Function, 그리고 일반 함수와 비슷해 보이지만 Generator는 yield 포함한다는 점에서 차이가 있다

Iterator와 generator의 차이점은 generator에서는 immutable 데이터 타입으로만 생성 가능한 점이다

특히 lazy evaluation 기법을 사용하기 때문에 메모리 사용이 효율적이고 속도가 빠르다는 장점이 있다

가지 방법으로 만들 있다

1. generator 표현식(tuple)

2, yield

주의 generator는 scope를 초과하면 StopIteration 에러가 뜬다

Tuple 방식

a = (x for x in range(10)) # 메모리 번지가 표시될 경우 iterator 또는 generator인 경우가 많다 
a
# <generator object <genexpr> at 0x7fad65698cd0>

Yield 방식

def x():
    yield 1
    yield 2
y = x()
next(y)
# 1

next(y)
# 2
%%writefile a.txt
abcdefg 
123123
dfskjfdskjl

# Writing a.txt
b = open('a.txt')
next(b)
# 'abcdefg \n'

b.close()
next(b) # 파일 읽기를 종료했기 때문에 더 이상 불러올 수 없다
ValueError: I/O operation on closed file.

Comprehension

여러 개의 데이터를 동시에 만들거나 변화시킬 수 있는 구문이다

Iterable한 객체를 생성하기 위한 방법

[종류] 1. List 2. Set 3. Dictionary

 

import tensorflow as tf 
tf.keras.preprocessing.image.I

 [x for x in range(10)]
 # [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
 
 [(x, y) for x in range(5) for y in range(6,10)] # 여러 개의 데이터를 동시에 생성할 때 
 [(0, 6),
 (0, 7),
 (0, 8),
 (0, 9),
 (1, 6),
 (1, 7),
 (1, 8),
 (1, 9),
 (2, 6),
 (2, 7),
 (2, 8),
 (2, 9),
 (3, 6),
 (3, 7),
 (3, 8),
 (3, 9),
 (4, 6),
 (4, 7),
 (4, 8),
 (4, 9)]
 
 [x for x in range(10) if x%2 ==0] 
 # [0, 2, 4, 6, 8]
 [x+1 for x in range(5)] # 기존에 있는 데이터를 변경시킬 때

Accumlation pattern

# 초기 값에서 값을 누적하며 저장하는 방식 
temp = 0 
for i in range(1, 11):
  temp += 1
temp
# 10
temp = []
for i in range(10):
  temp.append(i)
temp
# [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
import time
from functools import wraps 

def timeit(fn):
    @wraps(fn)
    def inner(*args, **kwargs):
        start_time = time.time()
        retval = fn(*args, **kwargs)
        duration = time.time() - start_time
        print("%s : %2.2f sec" % (fn.__name__, duration))
        return retval
    return inner
    
@timeit
def acc(n):
  temp = 0
  for i in range(n):
    temp += i
  return temp
acc(100000000)
# acc : 6.47 sec
# 4999999950000000

@timeit
def acc2(n):
  temp = 0
  temp = [temp + x for x in range(n)]
  return temp
# SyntaxError: invalid syntax

acc2(100000000)
# acc2 : 8.64 sec
# 4999999950000000

Python를 활용한 직업군

1. Automation

2. Data(big, ai)

3. Web

Data를 다루는 사람들의 관점에서는 comprehension은 쓰지 않는다.

왜냐하면 comprehension은 메모리에 한꺼번에 올리기 때문에 Bigdata에서는 적합하지 않다.

 

함수의 특징

1. return 반드시 있어야 한다 (return 하나이다)

- python에서는 return 생략하면 None 반환하도록 되어 있다

2. 함수 안에 다른 함수를 선언할 있다

3. Global, Local

- 함수 안에 없는 값을 return 하게 경우 가까운 Global 식별자를 return

- Global 식별자이름 하게 되면 접근, 수정이 가능하다

- 함수 밖에서 함수 안의 식별자에 접근, 수정이 불가능하다

 

First class function

함수를 값으로 사용할 수 있다

 

a = print
a('function')
# function

b = lambda x: x + 1 
b(4)
# 5

Higher order function

함수를 리턴값으로 쓰고, 함수를 인자로 쓰는 함수

First class function VS Higher order function

프로그래밍 언어가 first class function을 가진다는 말은 해당 프로그래밍 언어에서 함수를 값으로 취급 한다는 의미이다 Higher order function은 하나 이상의 함수를 인자로 받을 있고 함수를 반환 있는 함수를 의미한다

 

Higher order(고차)라는 개념은 수학적 의미의 함수와 같이 일반적으로 함수에 적용할 있고,

First class(일급)라는 개념은 프로그래밍 언어의 기능에만 연관이 있다

수학적 의미가 아닌 프로그래밍 관점에서 first class function을 지원하거나 지원하지 않거나 라고 말하는 것이 자연스러운 표현이다

 

따라서 Higher order function을 지원하지 않지만 first class function을 가진 언어 또는 higher order function은 지원하지만 first class function을 갖지 않는 언어가 존재할 있는가 싶을 만큼 둘은 밀접한 관련이 있다

 

Recursion

함수가 자기 자신을 호출하는 방법을 재귀라고 한다

※ 함수형 패러다임에서는 tail recursion elimination 기법을 제공하지만, python에서는 지원하지 않는다

 

def rr(x):
    if x==1:
        return 1
    return rr(x-1)*2
rr(6)
# 32

def fibb(n):
  x = 0
  1 =

Tail recursion elimination

Recursion(재귀)용법을 사용하면 함수가 자기 자신을 호출해야하기 때문에 호출할때 마다 중간 값을 저장하기 위한 메모리가 필요하고 호출 횟수가 늘어날 수록 실행 시간이 비약적으로 늘어나는 단점이 있다

그래서 이렇한 단점을 보완한 것이 바로 tail recursion elimination이다

 

반적으로 재귀 호출시 마지막(꼬리부분)에서 호출하는 경우를 반복문으로 바꾸어 결과를 누적시킴으로써 메모리 공간상의 효율도 챙기고 실행 시간도 많이 줄일수 있게 된다

# 일반적인 재귀용법 
def sumrange(m, n):
    if m <= n:
        return m + sumrange(m+1, n)
    else: 
        return 0 

sumrange(1,960)
# 461280
# 꼬리 재귀용법
def sumrange(m, n):
    def loop(m, total):
        if m <= n:
            return loop(m+1, m+total)
        else:
            return total
    return loop(m, 0)
sumrange(1,960)
# 461280

Map & Filter & Reduce (Higher order functioin)

Map

iterable에 있는 모든 요소에 function을 적용하여 결과를 반환한다

map 사용하면 lazy evaluation로 진행해서 메모리를 크게 절약할 있다

연산 결과는 map iterator 객체로 리턴한다

- Deep learning / Big data에서 주로 사용한다

def a(x):
    return x + 1

list(map(a,[1,2,3]))
# [2, 3, 4]

a = map(lambda x : x+1, [1,2,3,4]) # 로직에 집중할 수 있다는 장점이 있다 
'__next__' in dir(a)
# True

list(a)
# [2, 3, 4, 5]
import seaborn as sns
tips = sns.load_dataset('tips')
tips.tip.apply(lambda x:x+1) # 많은 데이터를 한번에 전처리 할때 자주 쓰인다 

0      2.01
1      2.66
2      4.50
3      4.31
4      4.61
       ... 
239    6.92
240    3.00
241    3.00
242    2.75
243    4.00
Name: tip, Length: 244, dtype: float64

Filter

predicate function 함수

iterable객체에 있는 요소중 조건에 맞게 True or False 되돌려 주는 함수

전체 데이터에서 필요한 데이터만 뽑아내는 함수

 

def b(x):
    return x > 3

list(filter(b,[1,2,3,4,5,6]))
# [4, 5, 6]

x = filter(lambda x:x>2, [1,2,3,4]) #필요한 데이터만 뽑아낸다 
list(x) 
# [3, 4]

Reduce

iterable객체의 여러개 값을 하나의 값으로 축약하여 표현할 사용한다

machine learning, deeplearning에서 자주쓰는 함수

통계에서 전체 값중에서 하나의 대표 값을 표현하는 경우가 많기 때문에 reduce를 자주 사용한다

 

from functools import reduce

reduce(lambda x,y:x+y,[1,2,3,4,5])
# 15
import tensorflow as tf 
tf.reduce_mean([1,2,3,]) # multiprocessin 기법을 사용할 수 있기 때문에 속도를 향상시킬수 있다 
<tf.Tensor: shape=(), dtype=int32, numpy=2>

def x(a): # 여기서 함수는 x 
  return a

 

파이썬 코드 스타일

PEP8 (Python Enhance Proposal)

파이썬은 PEP8은 파이썬 개선 제안서, 파이썬 코드를 어떻게 구상할 알려주는 스타일 가이드이다

PEP8 참고 문서: https://www.python.org/dev/peps/

# 귀도 반로섬(Guido van Rossum) #자애로운 종신 독재자

식별자 표기법 3가지

1. snake - 단어마다

_ (underscore) 붙여 이어나가는 표기법이다

- 모듈은 표기법을 권장한다

- 내장 함수도 보통 스네이크 표기법을 따른다

- ex) hello_world

2. camel

- 문자는 소문자로 표기하고, 다음 단어의 시작은 대문자로 표기한다

- 보통 파이썬에서는 카멜방식 채택

- 함수명은 표기법을 권장한다. ,소문자 + underscore를 쓰기도 한다

- ex) helloWorld

3. pascal(caps word)

- 문자를 대문자로 표기하고, 다음 단어의 시작도 대문자로 표기한다

- 클래스명은 표기법을 권장한다. , 이미 만들어져 있는 클래스는 소문자로 시작한다

- ex) HelloWorld

## 원활한 의사소통을 위해 pep8을 활용한다

Lambda

대입문과 정의문의 공동 목표는 재사용이었지만,

재사용할 필요가 없는 function이 필요할 사용하는 것이 lambda이다

- lambda 함수 이름이 존재하지 않는다 (익명함수)

- lambda 식처럼 사용 가능하다 (함수식)

- lambda 함수 호출시 사라지기 때문에 stack영역에 저장된다

반응형

+ Recent posts