original source : https://georgexyz.com/django-model-form-validation.html

django modelform을 이해하기 위해서는 model, form 각각 따로 이해하고 나서 종합적으로 이해한다. 이를 이해하고 나서는 formset에 대해 이해하고 modelformset에 대해 이해할수 있다.

Django model and form validation is a somewhat complicated and confusing topic in Django app development. A developer needs to understand several basic concepts such as model, model field, form, model form, etc. to have a good understanding of validation. Most Django books and online tutorials do not have a good discussion on validation.

Django official documentation has detailed descriptions on validation. However, the contents are dispersed on several places. This post describes the materials I have read on this topic.

Validator Function

The validator official documentation page is a good starting point to study model and form validation.

The validator function is a callable that takes a value and raises a ValidationError if not validated. Here is an example from the page:

from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _

def validate_even(value):
    if value % 2 != 0:
        raise ValidationError(
            _('%(value)s is not an even number'),
            params={'value': value},
        )

from django.db import models

class MyModel(models.Model):
    even_field = models.IntegerField(validators=[validate_even])

The subsection how validators are run on the validator page has three links.

  • The second link validating objects is about model validation. The link points to a subsection on the model instance reference page.
  • The first link form validation points to a separate page about form validation.
  • The third link goes to the ModelForm page.

Model Validation

A model’s full_clean() method performs model validation. The method calls three other methods:

  • clean_fields() method
  • clean() method, as a whole
  • validate_unique() method

The model save() method does NOT call full_clean() method automatically. A programmer needs to call it manually to trigger model validation like the below code.

try:
    article.full_clean()
except ValidationError as e:
    ...
    # handle the error

A stack overflow answer shows a typical pattern to conduct custom model validation. The model class overrides the clean() method to provide custom model validation and the save() method to call full_clean method. The example code is shown below:

class BaseModel(models.Model):
    # model fields 

    def clean(self, *args, **kwargs):
        # add custom validation here
        super(BaseModel, self).clean(*args, **kwargs)

    def save(self, *args, **kwargs):
        self.full_clean()
        super(BaseModel, self).save(*args, **kwargs)

Another stack overflow answer shows how to use custom model validation or simply use model field’s built-in validator.

Model field’s validation will not kick in unless the full_clean() method is explicitly called. For example, the p2.save() below would not raise an exception when called.

class PageModel(models.Model):
    name = models.CharField(max_length=50)
    slug = models.SlugField(max_length=50)

>>> from page.models import PageModel #page app name
>>> p1 = PageModel(name='Page1', slug='page1')
>>> p1.save()
>>> p2 = PageModel(name='Page2', slug='page2#$%')
>>> p2.save()        # no error
>>> p2.full_clean()  # raise exception

Checking clean_fields() method source code, it has the following lines. The f.clean(...) method calls validation method on a model field.

try:
    setattr(self, f.attname, f.clean(raw_value, self))
except ValidationError as e:
    errors[f.name] = e.error_list

Form Validation

While model validation is a subsection on a Django documentation page, the form validation is on a separate page. Form validation is normally executed when the is_valid() method is called on a form. A programmer can also trigger form validation by accessing errors attribute or call full_clean() method of a form.

Form validation has a series of steps:

  • to_python() method on a field, correct data type
  • validation() method on a field
  • run_validators() method on a field
  • clean() method on a Field subclass, which calls above three methods and returns the clean data
  • clean_<fieldname>() method has access to cleaned_data Python object and returns value that replaces data in cleaned_data
  • clean() method of form, for multiple fields

The same documetation page has several nice examples, which are based on the model shown below:

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    message = forms.CharField()
    sender = forms.EmailField()
    recipients = MultiEmailField()
    cc_myself = forms.BooleanField(required=False)

The same page points out that “there are special considerations when overriding the clean() method of a ModelForm subclass.”

Chapter 7 of Andrew Pinkham’s Django Unleashed book, titled allowing user input with forms, has good example on how to override clean_<fieldname> method. The discussion on model validation and form validation in this chapter is better than other Django books I have read.

ModelForm Validation

The form validation steps described in the previous section also apply to ModelForm validation. In addition to that, Model.full_clean() method is triggered after the form’s clean() method is called. So, model validation methods are not triggered by model save() method, but model validation methods are triggered by ModelForm validation. This stack overflow question discusses this exact issue. The accepted answer also has code example on model validation.

Error messages at the form field level take precedence over the error messages defined at the model field level.

class Model(**kwargs)

The keyword arguments are the names of the fields you’ve defined on your model. Note that instantiating a model in no way touches your database; for that, you need to save().

Model의 __init__을 이용해 Model이 생성될 때 어떤작업을 하고 싶은 경우 __init__을 override하는 것보다 . 아래와 같이 class method를 이용해 처리할것을 추천한다. 

1. Add a classmethod on the model class:

from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=100)

    @classmethod
    def create(cls, title):
        book = cls(title=title)
        # do something with the book
        return book

book = Book.create("Pride and Prejudice")

2. Add a method on a custom manager (usually preferred):

class BookManager(models.Manager):
    def create_book(self, title):
        book = self.create(title=title)
        # do something with the book
        return book

class Book(models.Model):
    title = models.CharField(max_length=100)

    objects = BookManager()

book = Book.objects.create_book("Pride and Prejudice")

Customizing model loading

Model이 loading될때 추가 작업을 하고싶은 경우 from_db()를 override한다.classmethod

Model.from_db(db, field_names, values)

참고자료) https://www.webforefront.com/django/modelmethodsandmetaoptions.html 하단부

Refreshing objects from database

If you delete a field from a model instance, accessing it again reloads the value from the database:

>>> obj = MyModel.objects.first()
>>> del obj.field
>>> obj.field  # Loads the field from the database

Model.refresh_from_db(using=None, fields=None)

if you need to reload a model’s values from the database, you can use the refresh_from_db() method.

Validating objects

There are three steps involved in validating a model:

  1. Validate the model fields – Model.clean_fields()
  2. Validate the model as a whole – Model.clean()
  3. Validate the field uniqueness – Model.validate_unique()

All three steps are performed when you call a model’s full_clean() method.


Model.full_clean(exclude=None, validate_unique=True)

This method calls Model.clean_fields(), Model.clean(), and Model.validate_unique() (if validate_unique is True) 유효성 검사중에 어디서든 ValidationError raise되면 유효성검사가 실패한것이 된다.

The optional exclude argument can be used to provide a list of field names that can be excluded from validation and cleaning. ModelForm uses this argument to exclude fields that aren’t present on your form from being validated since any errors raised could not be corrected by the user.

Note that full_clean() will not be called automatically when you call your model’s save() method. 

full_clean()의 호출방법의 예는 아래와 같다.

from django.core.exceptions import ValidationError
try:
    article.full_clean()
except ValidationError as e:
    # Do something based on the errors contained in e.message_dict.
    # Display them to a user, or handle them programmatically.
    pass


Model.clean_fields(exclude=None)

This method will validate all fields on your model. The optional exclude argument lets you provide a list of field names to exclude from validation. It will raise a ValidationError if any fields fail validation.

The second step full_clean() performs is to call Model.clean(). This method should be overridden to perform custom validation on your model.

Model.clean()

This method should be used to provide custom model validation, and to modify attributes on your model if desired. For instance, you could use it to automatically provide a value for a field, or to do validation that requires access to more than a single field: 아래와 같이 여러 항목을 이용해서 validate하는 경우 clean()에서 작업한다.

import datetime
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.translation import gettext_lazy as _

class Article(models.Model):
    ...
    def clean(self):
        # Don't allow draft entries to have a pub_date.
        if self.status == 'draft' and self.pub_date is not None:
            raise ValidationError(_('Draft entries may not have a publication date.'))
        # Set the pub_date for published items if it hasn't been set already.
        if self.status == 'published' and self.pub_date is None:
            self.pub_date = datetime.date.today()

To assign exceptions to a specific field, instantiate the ValidationError with a dictionary, where the keys are the field names. 예를 들어 pub_date field의 문제의 경우 아래와 같이 해준다.

class Article(models.Model):
    ...
    def clean(self):
        # Don't allow draft entries to have a pub_date.
        if self.status == 'draft' and self.pub_date is not None:
            raise ValidationError({'pub_date': _('Draft entries may not have a publication date.')})

Model.clean()에서 여러 에러가 발생하는 경우 아래와 같이  you can also pass a dictionary mapping field names to errors:

raise ValidationError({
    'title': ValidationError(_('Missing title.'), code='required'),
    'pub_date': ValidationError(_('Invalid date.'), code='invalid'),
})

ModelForm에서 Meta.fields or Meta.exclude를 이용해 제외된 field의 ValidationError를 raise해도작동이 되지 않고 ValueError가 발생하게 되는데 이에 대한 우회방법은 아래와 같다. 

To work around this dilemma, instead override Model.clean_fields() as it receives the list of fields that are excluded from validation. For example:

class Article(models.Model):
    ...
    def clean_fields(self, exclude=None): #마치 제외된 field가 없는척한다.
        super().clean_fields(exclude=exclude)
        if self.status == 'draft' and self.pub_date is not None:
            if exclude and 'status' in exclude:
                raise ValidationError(
                    _('Draft entries may not have a publication date.')
                )
            else:
                raise ValidationError({
                    'status': _(
                        'Set status to draft if there is not a '
                        'publication date.'
                     ),
                })

Model.validate_unique(exclude=None)

model의 field중에 unique, unique_together ,  unique_for_date|month|year 이 설정되있는 경우 호출되며 어떻게 unique를 결정할지에 대한 내용이 들어 간다.



Saving objects

To save an object back to the database, call save():

Model.save(force_insert=False, force_update=False,using=DEFAULT_DB_ALIAS, update_fields=None)



Auto-incrementing primary keys

AutoField — an auto-incrementing primary key — then that auto-incremented value will be calculated and saved as an attribute on your object the first time you call save():

>>> b2 = Blog(name='Cheddar Talk', tagline='Thoughts on cheese.')
>>> b2.id     # Returns None, because b2 doesn't have an ID yet.
>>> b2.save()
>>> b2.id     # Returns the ID of your new object.

There’s no way to tell what the value of an ID will be before you call save()

For convenience, each model has an AutoField named id by default unless you explicitly specify primary_key=True on a field in your model. See the documentation for AutoField for more details. 특별히 특정 field에  primary_key=True 를 설정하지 않으면 id라는 AutoField 가 만들어 지게 된다.



Model.pk

자동으로 만들어진 AutoField이건 개발자가 primary_key=True 설정해 지정된 field이건 간에 간단히 pk 이름으로 접근할수 있다.



AutoField 로 정의 되어있더라도 개발자가 임의로 값을 할당할수 있다.

AutoField 이더라도 아래와 같이 처음 obj가 생성될때 넣어주면 이미 있지 않은 id라면 만들어진다. 이미 있던 id라면 내용이 update가 된다.

>>> b3 = Blog(id=3, name='Cheddar Talk', tagline='Thoughts on cheese.')
>>> b3.id     # Returns 3.
>>> b3.save()
>>> b3.id     # Returns 3.
b4 = Blog(id=3, name='Not Cheddar', tagline='Anything but cheese.')
b4.save()  # Overrides the previous blog with ID=3!

original source : https://docs.djangoproject.com/en/3.0/topics/signals/

Signals

Django includes a “signal dispatcher” which helps allow decoupled applications get notified when actions occur elsewhere in the framework. In a nutshell, signals allow certain senders to notify a set of receivers that some action has taken place. They’re especially useful when many pieces of code may be interested in the same events.

Django provides a set of built-in signals that let user code get notified by Django itself of certain actions. These include some useful notifications:

Sent before or after a model’s save() method is called.

Sent before or after a model’s delete() method or queryset’s delete() method is called.

Sent when a ManyToManyField on a model is changed.

Sent when Django starts or finishes an HTTP request.

See the built-in signal documentation for a complete list, and a complete explanation of each signal.

You can also define and send your own custom signals; see below.

Listening to signals

To receive a signal, register a receiver function using the Signal.connect() method. The receiver function is called when the signal is sent. All of the signal’s receiver functions are called one at a time, in the order they were registered.

(Signal에 특정 sender에만 반응하는 receiver를 연결, dispatch_uid를 이용 중복을 막는다.)

Signal.connect(receiver, sender=None, weak=True, dispatch_uid=None)

[source]

Parameters:

  • receiver – The callback function which will be connected to this signal. See Receiver functions for more information.
  • sender – Specifies a particular sender to receive signals from. See Connecting to signals sent by specific senders for more information.
  • weak – Django stores signal handlers as weak references by default. Thus, if your receiver is a local function, it may be garbage collected. To prevent this, pass weak=False when you call the signal’s connect() method.
  • dispatch_uid – A unique identifier for a signal receiver in cases where duplicate signals may be sent. See Preventing duplicate signals for more information.

Let’s see how this works by registering a signal that gets called after each HTTP request is finished. We’ll be connecting to the request_finished signal.

Receiver functions

First, we need to define a receiver function. A receiver can be any Python function or method:

def my_callback(sender, **kwargs):
    print("Request finished!")

Notice that the function takes a sender argument, along with wildcard keyword arguments (**kwargs); all signal handlers must take these arguments.

We’ll look at senders a bit later, but right now look at the **kwargs argument. All signals send keyword arguments, and may change those keyword arguments at any time. In the case of request_finished, it’s documented as sending no arguments, which means we might be tempted to write our signal handling as my_callback(sender).

This would be wrong – in fact, Django will throw an error if you do so. That’s because at any point arguments could get added to the signal and your receiver must be able to handle those new arguments.

Connecting receiver functions

두가지 방법이 있으며 하나는 Signal.connect()를 이용하는 것과 decorator를 이용하는 것이다.

There are two ways you can connect a receiver to a signal. You can take the manual connect route:

from django.core.signals import request_finished

request_finished.connect(my_callback)

Alternatively, you can use a receiver() decorator:

receiver(signal)

[source]

Parameters:signal – A signal or a list of signals to connect a function to.

Here’s how you connect with the decorator:

from django.core.signals import request_finished
from django.dispatch import receiver

@receiver(request_finished)
def my_callback(sender, **kwargs):
    print("Request finished!")

Now, our my_callback function will be called each time a request finishes.

Where should this code live?

Strictly speaking, signal handling and registration code can live anywhere you like, although it’s recommended to avoid the application’s root module and its models module to minimize side-effects of importing code.

(receiver는 보통 signals이라는 module에 넣어 보관한다.)

In practice, signal handlers are usually defined in a signals submodule of the application they relate to. Signal receivers are connected in the ready() method of your application configuration class. If you’re using the receiver() decorator, import the signals submodule inside ready().

https://youtu.be/Kc1Q_ayAeQk?t=632

Note

The ready() method may be executed more than once during testing, so you may want to guard your signals from duplication, especially if you’re planning to send them within tests.

Connecting to signals sent by specific senders

(sender를 지정해서 특정 작업에만 반응하게 한다.)

Some signals get sent many times, but you’ll only be interested in receiving a certain subset of those signals. For example, consider the django.db.models.signals.pre_save signal sent before a model gets saved. Most of the time, you don’t need to know when any model gets saved – just when one specific model is saved.

In these cases, you can register to receive signals sent only by particular senders. In the case of django.db.models.signals.pre_save, the sender will be the model class being saved, so you can indicate that you only want signals sent by some model:

from django.db.models.signals import pre_save
from django.dispatch import receiver
from myapp.models import MyModel


@receiver(pre_save, sender=MyModel)
def my_handler(sender, **kwargs):
    ...

The my_handler function will only be called when an instance of MyModel is saved.

Different signals use different objects as their senders; you’ll need to consult the built-in signal documentation for details of each particular signal.

Preventing duplicate signals

In some circumstances, the code connecting receivers to signals may run multiple times. This can cause your receiver function to be registered more than once, and thus called multiple times for a single signal event.

If this behavior is problematic (such as when using signals to send an email whenever a model is saved), pass a unique identifier as the dispatch_uid argument to identify your receiver function. This identifier will usually be a string, although any hashable object will suffice. The end result is that your receiver function will only be bound to the signal once for each unique dispatch_uid value:

from django.core.signals import request_finished

request_finished.connect(my_callback, dispatch_uid="my_unique_identifier")

Defining and sending signals

(커스텀 Signal 이용하기)

Your applications can take advantage of the signal infrastructure and provide its own signals.

When to use custom signals

Signals are implicit function calls which make debugging harder. If the sender and receiver of your custom signal are both within your project, you’re better off using an explicit function call.

Defining signals

class Signal(providing_args=list)

[source]

All signals are django.dispatch.Signal instances. The providing_args is a list of the names of arguments the signal will provide to listeners. This is purely documentational, however, as there is nothing that checks that the signal actually provides these arguments to its listeners.

For example:

import django.dispatch

pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])

This declares a pizza_done signal that will provide receivers with toppings and size arguments.

Remember that you’re allowed to change this list of arguments at any time, so getting the API right on the first try isn’t necessary.

Sending signals

There are two ways to send signals in Django.

Signal.send(sender, **kwargs)

[source]

Signal.send_robust(sender, **kwargs)

[source]

To send a signal, call either Signal.send() (all built-in signals use this) or Signal.send_robust(). You must provide the sender argument (which is a class most of the time) and may provide as many other keyword arguments as you like.

For example, here’s how sending our pizza_done signal might look:

class PizzaStore:
    ...

    def send_pizza(self, toppings, size):
        pizza_done.send(sender=self.__class__, toppings=toppings, size=size)
        ...

Both send() and send_robust() return a list of tuple pairs [(receiver, response), ... ], representing the list of called receiver functions and their response values.

send() differs from send_robust() in how exceptions raised by receiver functions are handled. send() does not catch any exceptions raised by receivers; it simply allows errors to propagate. Thus not all receivers may be notified of a signal in the face of an error.

send_robust() catches all errors derived from Python’s Exception class, and ensures all receivers are notified of the signal. If an error occurs, the error instance is returned in the tuple pair for the receiver that raised the error.

The tracebacks are present on the __traceback__ attribute of the errors returned when calling send_robust().

Disconnecting signals

Signal.disconnect(receiver=None, sender=None, dispatch_uid=None)

[source]

To disconnect a receiver from a signal, call Signal.disconnect(). The arguments are as described in Signal.connect(). The method returns True if a receiver was disconnected and False if not.

The receiver argument indicates the registered receiver to disconnect. It may be None if dispatch_uid is used to identify the receiver.

8 1 Generative vs Discriminative Models

https://youtu.be/YQClUDd9ff4

(ML 1.5) Generative vs discriminative models

https://youtu.be/oTtow2Ui8vg

image

7 2018 05 09 Data Science Generative Vs Discriminative Models

https://youtu.be/gwV7spVO5Z0

image

generative에서는 일단 확률밀도함수를 구한다. 그리고 이를 이용해서 각 지점의 확률값을 얻게 된다.

image

discriminative는 예상값과 실제값과의 손실을 비교해서 가장 손실이 적은 모델을 찾아간다.

image

Machine Learning interview questions-what is difference between generative and discriminative model

https://youtu.be/uhRPeyzTZ9o

image

https://m.blog.naver.com/PostView.nhn?blogId=2feelus&logNo=221078340870&proxyReferer=https%3A%2F%2Fwww.google.com%2F

Discriminative Learning Algorithms

머신러닝의  분류 알고리듬을 다음과 같이 풀어서 설명할수 있다.

어떤 입력값(input) x가 주어졌을때 그 결과값(label)이 y일 확률을 알아내는 것

=>  p( y | x )

=> x라는 값들이 충분히 의미있는 데이터일때 그 데이터를 기반으로  y가 0일지 1일지를 확률 적으로 예측

=> 결과적으로 구분선을 찾아내는 것이 중요하다.

이렇게 입력값과 출력값을 직접적으로 연결시키는 방식을 Discriminative(변별) Algorithm이라고 한다.

Discriminative방식의 머신러닝은 다음의 과정으로 이루어진다.

1. 특정 입력값X에 대한 조건부 확률 분포를(Conditional Probability Distribution) 만들어낸다.

=> 조건부 확룰 분포는 입력값 x가 특정 값이라는 조건으로 주어질때의 Y값의 분포도를 기술하는 데이터형식이다.

2. 조건부 확률 분포에 근거해서 x input과 y label(output)로 벡터를 만들어낸다.

3. x값들의 성질(결과값, 즉 y값)이 0인 것 들과 1인 것들을 잘 구분할수 있는 선(직선 혹은 곡선, decision boundary)을 만들어낸다.

4. 만들어진 선을 기반으로 새로운 데이터(x’)가 입력되었을때 선으로부터 음 혹은 양의 방향으로의 거리를 재어 확률을 구한다.

그림으로 살펴보면 다음과 같다.( 모든 원들을 x를 의미, 노랑/파랑은 y=0/y=1을 의미, 곡선은 구분선을 의미, 세모는 새로운 입력을, distant는 선분으로부터 음/양의 거리를 의미)

대표적인 방식이 Logistic Regression이다.  그 외에도 Conditional Random Field, Support Vector Machine, Linear Regression, Neural Network 등의 알고리즘들이 있다.

장점:

데이터가 충분할경우 좋은 성능을 보여준다.

단점:

데이터를 구분하는데 목표를 두고 있으므로, 데이터가 실제 어떤 모습인지 본질을 이해하기는 어렵다.

Generative Learning Algorithms

그런데 이러한 Discriminative 방식과는 다른 방식의 분류 알고리듬이 있다. 구분해내는 것을 넘어서 분류 카테고리에 맞는 데이터를 생성해내는 방식이다.

생성되는 입력과 결과 데이터는 분류 클래스별로 특정한 통계적 분포를 따른다고 가정하는 방식이다.

다음과 같이 풀어서 설명할수 있다.

=>  입력값 x와 결과값(라벨)y가 주어질때,  이들은 일정한 분포 규칙속에서 존재한다. 그 분포 규칙은 분류 클래스마다 다르다.

=> 이러한 분포 규칙은 Normal Distribution등의 통계적인 방법론을 따른다.

=>  분포규칙은 수식적으로는 결합 확률 분포 – p( x , y)  로 표현한다. ( 결합 확률 분포- joint probability distribution )

=> 실제로 분류에  필요한 사후확률 p ( y | x)은 p(x,y)로 부터 유도할수 있다. (변경과정은 아래 부록1에 )

=> 핵심은 카테고리별로  분포규칙을 표현하는  확률 분포 모델의 파라미터들을 찾아 내는 것이다.

이렇게 입력값과 출력값 사이에 분포규칙의 개념을 적용한 방식을 Generative(생성) Algorithm이라고 한다.

Generative방식의 머신러닝은 다음의 과정으로 이루어진다.

1. 주어진 데이터와 결과값을 이용해 모든 x와 모든 y에 대해 결합 확률 분포 (Joint Probability Distribution)를 만든다.

2. 결합확률 분포로 부터 어떤 확률 분포모델이 데이터 분포모델에 적한한지 측정(estimation)한다. (어려운 과정)

3. 측정해낸 확룰 분포모델을 기반으로 사후확률을 알아낼수 있다.

p(y|x) =  p(x|y) * p(y) / p(x)

사후 확률 = 결합확률(p(x,y))  / p(x) –

4.즉  x가 주어질 경우, y의 확률을 예측하려면,  x,y에 대한 결합확률분포공식(모델)과 x의 확률(경계확률)을 알면 구할 수 있다.

그림으로 살펴보면 다음과 같다. (작은 원들을 x를 의미, 노랑/파랑은 y=0/y=1을 의미, 세모는 새로운 입력을, 큰 타원은 확률 분포를 의미)

대표적인 방식은 Naive Bayes가 있으며 이외에도 Gaussian discriminant Analysis(GDA), Gaussian Mixture Model(GMM), Hidden Markov Model(HMM), Latent Dirichlet Allocation(LDA), Restricted Boltzmann machine(RBM), Generative Adversarial Network(GAN)등이 있다.

Naive Bayes의 경우에도 주어진 관측데이터 결합확률 분포를 통해 확률모델로 만들어내기때문에 Generative Model이다.

장점:

데이터 셋이 적어도 할만하다.

데이터 자체의 특성을 파악하기에 좋다.

데이터를 생성해 내어 새로운 결과물을 얻어낼수 있다.

단점:

데이터가 많은 경우, Discriminative에 비해 성능이 떨어 질수 있다.

결합확률분포표 와 조건부확률분포표

아래는 입력값 X가 0 ~ 2에 걸쳐서 이산분포하고, 결과값 Y가 0~2에 결쳐서 이산분포할때 의 결합확률과 조건부 확률을 비교한 것이다.

문제를 통해서 결합확률과 조건부확률간의 비교를 해보자.

결합확률분포표(Joint Probability Distribution)


조건부확률분포표(Conditional Probability Distribution)

X가 1로 주어졌을때, Y가 1일확률은 0.8로서 다른 Y class들(0,2)에 비해서 가장확률이 높다.  (checksum이 0.30에서 1.0으로 바뀐것을 확인)

즉 x가 1일때 예측값은 1이다. 수식은 다음과 같다. argmax는 입력값중에 가장 큰 값을 선택하라는 의미이다.

class = argmax( P(Y|X=1) ) = 1

부록1) 베이즈룰에 따라 결합확률에서 사후확률로 변경되는 과정

=> p(x,y) = p(y|x) * p(x)  베이즈룰에 따라, 결합확률은  x(input data)가 y(label) 일때의 사후 확률(posterior)과  x의 경계확률의 곱으로 표현할수 있다.

=> p(x,y) = p(x|y) * p(y)  베이즈룰에 따라, 결합확률은  y(label)가 주어졌을때  x(input) 일때의 가능도(likelihood)와  y의 사전확률(prior)의 곱으로 표현할수 있다.

=> p(x,y) = p(y|x) * p(x) = p(x|y) * p(y) 라는 베이즈 확률 공식에 따라, p(y|x) =   p(x|y) * p(y) / p(x) 로 표현할수 있다.

=> p(y|x) = p(x|y) * p(y) / p(x) .

즉 입력값 x가 주어질때의 사후확률은 라벨 y 가 입력값 x 일 경우의 가능도와 라벨 y 의 사전확률을 곱한것을 x 일경우의 모든 경계확률로 나눈것이다.

참고)

Open AI의 Generative Model – “What I Cannot Create, I do not understand”(만들지 못하면 이해하지 못한거야) – https://blog.openai.com/generative-models/

Andrew Ng의 표준분포를 이용한 Generative Learning algorithms  – http://cs229.stanford.edu/notes/cs229-notes2.pdf

Wikipedia 베이즈 확률론 –https://ko.wikipedia.org/wiki/%EB%B2%A0%EC%9D%B4%EC%A6%88_%ED%99%95%EB%A5%A0%EB%A1%A0

결합확률분포표과 조건부 확률분포표의 계산 – https://onlinecourses.science.psu.edu/stat414/node/116

의미적 의존 링크 토픽 모델을 이용한 생물학 약어 중의성 해소 – http://kiise.or.kr/e_journal/2014/9/JOK/pdf/07.pdf

image
image

linear regression은 기본적으로 위의 조건을 만족한다고 가정하고 시작한다. 즉 위의 성질을 가지는 경우에 linear regression 알고리즘을 사용할수 있다.

DUMMY VARIABLES

image

category를 나누는 variable이 있는 경우 각 category value별로 칼럼을 추가해 준다. 이강의경우는 두개의 category values가 있는데 이중하나만 d1으로 사용해주고 다른 하나는 constant b0에서 처리하게 했다.

DUMMY VARIABLE TRAP

image

dummy variable의 갯수( 하나의 category내 존재하는 값의 종류 갯수 )에서 1을 뺀 갯수만큼만 추가해준다. 예를 들어 50개주의 이름이라면 49개만 만든다. 또 다른 종류의 카테고리가 존재한다면 여기에도 똑같은 규칙을 적용한다. 

BUILDING A MODEL 

( 어떤 방법으로 model을 만들지에 대한 방법론 또한 어떻게 중요하지 않는 feature를 알고리즘에서 제거하는지에 대한 방법로 )

image

때때로 bidirectional elimination만을 stepwise regression이라고 말하기도 한다. 

image

모든 주어진 feature들을 이용해서 만드는 경우. 주어진 상황이 그렇게 해야만 하거나 이미 알고있는 정보에 따르면 모든 feature가 중요하게 작용한다는 판단이 있는경우, 또 backward elimination작업의 전단계 준비작업으로서 사용되기도 한다.

아래그림들에서 fit이라는 용어가 나오는데 거칠게 이해하면 training이라고 이해 할수 있다. 참조) https://stackoverflow.com/questions/45704226/what-does-fit-method-in-scikit-learn-do

image

참고사항)

p value 가 커지면 그 항목은 결과값에 별 영향을 끼치지 않는다고( not significant ) 보고 feature에서 제외한다. 

https://youtu.be/128yz0OCG-I?t=190

https://www.quora.com/Do-machine-learning-professionals-care-about-p-values

image

image

처음 시작은 feature 하나로 시작한다. 그리고 가장 낮은 p value를 가지는 feature를 선택한다. 그리고 여기에 다른 feature를 하나더 덧붙인다. 또 가장 낮은 p value를 가지는 알고리즘을 택하고 여기하 하나 더 feature를 덧붙인다.

image

image

모든 경우의 수에 해당하는 알고리즘을 테스트해서 결정하는 방법이다.

실제 CODING 예시 – multiple linear regression

Dummy variable trap을 방지하기 위한 작업. 이 강의 예시에서 dummy variable 3개가 데이터셑 앞에 생기는데 맨 앞 칼럼은 제거하고 2개만 인정하는 과정이다. 보통 이렇게 하지 않아도 라이브러리가 알아서 dummy variable trap방지를 위한 작업을 하는데 여기서는 교육목적으로 다시 보여주고 있다.

image
image
image

multiple linear regression도 single linear regression과 과정은 대부분 똑같다. 라이브러리가 차이나는 부분을 스스로 많이 커버한다.

image

 test set 의 예상결과를 얻는 과정이다. multiple linear regression도 single linear regression과 과정은 똑같다. 다만 multiple linear regression의 경우 다차원의 데이터이므로 시각적으로 그래프로 보여주기는 힘들다.

Backward elimination

사전준비 작업

image

backward elimination을 위한 라이브러리를 import한다.

기존 데이터셑에는 없던 x0 feature값은 데이터 셑 맨 앞에 추가하는 작업이다. x0 는 1 이며 이는 예상값을 계산하는데 constant와 곱해져서 더해진다. append를 통해 칼럼을 더한다. 맨앞에 x0의 값이 오게끔하기 위해 1로만 이루어진 1열 칼럼을 만들고 여기에 기존 데이터셑을 더한다. ones method는 1만 들어있는 50줄 1칼럼 matrix를 만든다.  

image

step2 를 위한 작업은 아래와 같다.

image

이전까지 사용했던 sklearn의 LinearRegression 대신 OLS를 사용했다. OLS는 ordinary least squares 이며 linear regression과 같은 의미로 사용되곤한다.

X[: , [0, 1, 2, 3, 4, 5]] 는 모든행, 0 1 2 3 4 5 칼럼을 가지는 matrix가 된다. 나중에 p value가 특정값보다 큰경우 해당 칼럼을 제거할 것이다. 

OLS obj의 summary()를 이용 추가정보를 출력 한다. 그중 아래와 같이 p value를 확인 가장 높은 p value ( 연관성이 떨어지는 )를 가지는 칼럼을 확인한다. 그 칼럼을 삭제할 것이다.

기존 코드에서 연관성이 떨어지는 칼럼을 삭제하고 새로 model을 만든다.