<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Loss under a Local Perceptron model
Step1:  Loss under a Mixture of Gaussians model
Step2:  We use autograd for functions that deliver gradients of those losses
Step3:  Just a pretty display
Red and Black are target 0 and 1 patterns respectively.
They will get "filled in" once the perceptron is getting them correct.
Step4:  Learning, starting from random weights and bias.
<ASSISTANT_TASK:>
Python Code:
def sigmoid(phi):
    return 1.0/(1.0 + np.exp(-phi))
def calc_prob_class1(params):
    # Sigmoid perceptron ('logistic regression')
    tildex = X - params['mean']
    W = params['wgts']
    phi = np.dot(tildex, W)
    return sigmoid(phi)  # Sigmoid perceptron ('logistic regression')
def calc_membership(params):
    # NB. this is just a helper function for training_loss really.
    tildex = X - params['mean']
    W, r2, R2 = params['wgts'], params['r2'], params['R2']
    Dr2 = np.power(np.dot(tildex, W), 2.0)
    L2X = (np.power(tildex, 2.0)).sum(1)
    DR2 = L2X - Dr2
    dist2 = (Dr2/r2) + (DR2/R2)  # rescaled 'distance' to the shifted 'origin'
    membership = np.exp(-0.5*dist2)
    #print(membership)
    return np.array(membership)
def classification_loss(params):
    membership = calc_membership(params)
    Y = calc_prob_class1(params)
    return np.sum(membership*(Targ*np.log(Y) + (1-Targ)*np.log(1-Y)))
Explanation: Loss under a Local Perceptron model
End of explanation
def MoG_loss(params):
    membership = calc_membership(params)
    return np.sum(membership)
Explanation: Loss under a Mixture of Gaussians model
End of explanation
classification_gradient = grad(classification_loss)
MoG_gradient = grad(MoG_loss)
Explanation: We use autograd for functions that deliver gradients of those losses
End of explanation
# Be able to show the current solution, against the data in 2D.
def show_result(params, X, Targ):
    print("Parameters:")
    for key in params.keys():
        print(key,'\t', params[key])
    print("Loss:", training_loss(params))
    membership = calc_membership(params)
    Y = calc_prob_class1(params)
    pl.clf()
    marksize = 8
    cl ={0:'red', 1:'black'}
    for i, x in enumerate(X):
        pl.plot(x[0],x[1],'x',color=cl[int(Targ[i])],alpha=.4,markersize=marksize)
        pl.plot(x[0],x[1],'o',color=cl[int(Targ[i])],alpha=1.-float(abs(Targ[i]-Y[i])),markersize=marksize)
        
    pl.axis('equal')
    s = X.ravel().max() - X.ravel().min()
    m, w = params['mean'], params['wgts']
    # Show the mean in blue
    #pl.arrow(0, 0, m[0], m[1], head_width=0.25, head_length=0.5, fc='b', ec='b', linewidth=1, alpha=.95)
    # Show the perceptron decision boundary, in green
    pl.arrow(m[0]-w[0], m[1]-w[1], w[0], w[1], head_width=s, head_length=s/5, fc='g', ec='g', linewidth=3, alpha=.5)
    pl.show()  
Explanation: Just a pretty display
Red and Black are target 0 and 1 patterns respectively.
They will get "filled in" once the perceptron is getting them correct.
End of explanation
def do_one_learning_step(params,X,Targ,rate):
    grads = classification_gradient(params)
    params['wgts'] = params['wgts'] + rate * grads['wgts']   # one step of learning
    params['mean'] = params['mean'] + rate * grads['mean']   # one step of learning
    return (params)
init_w = rng.normal(0,1,size=(Nins))
init_m = 4.*rng.normal(0,1,size=(Nins))
rate = 0.5 / Npats
params = {'wgts':init_w, 'mean':init_m, 'r2':1000.0, 'R2':1000.0}
for t in range(250):
    params = do_one_learning_step(params,X,Targ,rate)
show_result(params, X, Targ)
Y = sigmoid(np.dot(X-params['mean'], params['wgts'])) 
print('vanilla loss: ', np.sum(Targ*np.log(Y) + (1-Targ)*np.log(1-Y)))
Explanation: Learning, starting from random weights and bias.
End of explanation
<END_TASK>