```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
setwd("~/Github/bigdata/lectures/l18/")
library(tidyverse)
```

# Today's Class {.smaller}

0. Themes: OOS, Loss, Models as Legos
1. Week 1: Errors, FDR, Loss
2. Week 2: Regressions (linear, logit), Deviance, OOS, Bootstrap
3. Week 3: AIC, Stepwise Regression, LASSO, CV (K-fold)
4. Week 4: KNN, Classification, ROC, Multinomial
5. Week 5: Trees, Bagging, Ensembles, Forests
6. Week 6: Boosting, More Ensembles, Rolling Block CV
7. Week 7: Cleaning Data: pivot, join, aggregate, winsorize
8. Week 8: RCTs, ATEs, CATEs, Targeting
9. Week 9: Neural nets, SGD, Optimization, Review

# Themes

## OOS

1. Out of sample predictive performance is the one true metric
  - Cross validation and its variants are incredible tools
  - Avoid 'contaminating' training data with test data
  - If your OOS predictions are bad, why would your inference be good?

## Costs

2. The model we estimate _depends_ on the costs of the mistakes we could make
  - We often face different risks in one direction or the other. Don't ignore that.
  
## Selection vs Aggregation

3. Choosing between models is hard. Aggregating them is easy
  - Why not use them all?
  - OOS performance to help downweight useless models
  - Lots of dumb models can make for one good model

## Uncertainty

4. Multiple sources of uncertainty
  - In-side the final model
  - Our model choice procedure
  - Our data's reliability

## Models are Legos

5. Models are like building blocks
  - We can build crazy things by combining them
  - and we can decide we don't trust a bridge made of them



# Week 1: Errors, FDR, Loss

## Large Scale Testing 

We wish to test $K$ simultaneous null hypothesis:
$$H0_1,H0_2,...,H0_K $$
Out of the $K$ null hypothesis, $N_0$ are true nulls and $N_1 = K-N_0$ are false -- i.e. there is an effect. 

![](../l1/fdr.png){width=80%}

## False Discovery Rate

FD Proportion = False positives / #Significant = $\frac{FD}{R}$  
**We can't know this.** 

We can control its expectation  though:
  False Discovery Rate, $FDR = E[FDP]$. 
  
If all tests are tested at $\alpha$ level, we have $\alpha = E[FD/N_0]$, whereas $FDR = E[FD/R]$

We can find in-sample analogues (ish) of these things. 

## FDR Control

Suppose we want to know that $FDR \leq q = 0.1$.


**Benjamini + Hochberg Algorithm**

1. Rank your p-values smallest to largest.
2. Set p-value cutoff as $\alpha^* = max\{p_{(k)}: p_{(k)} \leq q \frac{k}{K}\}$

Then $FDR \leq q$ -- assuming approximate independence between tests. 

## BH

![](../l2/sims/BH.png){width=110%}

## Loss
At its simplest, a loss function maps the truth, and your prediction, into how unhappy you are about your error. 

$$L(y,\hat{y}) = ????$$
The most common class loss function is the squared error loss. 

$$L_2(y,\hat{y}) = l_2(y-\hat{y}) = l_2(e) = e^2$$
This loss only cares about the magnitude of the error, not its location, not its direction. It also penalizes larger loss more than smaller loss. This is not always a reasonable set of choices.


# Week 2: Regressions (linear, logit), Deviance, OOS, Bootstrap

## Regression

Many problems involve a response or outcome (`y`),  
And a bunch of covariates or predictors (`x`) to be used for regression.

A general tactic is to deal in averages and lines. 

$$E[y|\textbf{x}] = f(\textbf{x}'\beta)$$

Where  $\textbf{x} = [1,x_1,x_2,x_3,...,x_p]$  is our vector of $p$ covariates.
$\beta = [\beta_0,\beta_1,\beta_2,...,\beta_p]$ are the corresponding coefficients.  
The product $x'\beta = \beta_0+\beta_1 x_1 + \beta_2 x_2+\cdots+\beta_p x_p$.

For simplicity we denote $x_0 = 1$ to estimate intercepts.


## Linear Regression

Linear regression takes this seriously, and adds that $E[Y|X] = X'\beta$. 

```{r, echo=F,out.width="100%",fig.align="center", message=F}
set.seed(203)
x = rnorm(200,mean=1.5,sd=0.5)
y = 0.8*x+rnorm(200,sd=0.5)+2
df = data.frame(x=x,y=y)
df2 = data.frame(x=c(1,2,2),y=c(2.8,2.8,3.6))

ggplot() + 
  geom_point(data=df,aes(x=x,y=y),alpha=0.1)+
  geom_abline(aes(intercept = 2,slope=0.8)) +
  geom_path(data=df2,aes(x=x,y=y),col=2,lty=1,lwd=2) + 
  xlim(0,3)+ylim(0,5) + 
  annotate("errorbar",x=2.2,ymin=2.8,ymax=3.6,width=0.2)+
  annotate("text",x=2.4,y=3.2,label="beta",parse=T,size=10)+
  annotate("text",x=2.7,y=3.2,label="= 0.8",size=10)+
  annotate("errorbar",y=2.5,xmin=1,xmax=2,width=0.2) +
  annotate("text",y=2.1,x=1.5,label="1",size=10) +
  annotate("errorbar",x=0.2,ymin=0,ymax=2,width=0.2) + 
  annotate("text",x=0.7,y=1.2,label="Intercept = 2",size=7)+
  annotate("point",x=0,y=c(0,2),col=2,size=5)
```

## Logistic Regression

Building a linear model for **binary data**.  
Recall our original specification: $E[Y|X] = f(x'\beta)$

The Response $y$ is 0 or 1, leading to a conditional mean:
$$E[y|x] = P[y=1|x]\times 1 + P[y=0|x]\times 0 = P[y=1|x]$$
$\implies$ The expectation is a probability. 

The 'logit' link is common, for a few reasons. One big reason? 
$$log\left(\frac{p}{1-p}\right) =\beta_0+\beta_1x_1+...+\beta_Kx_K$$
This is a linear model for log odds.

## Deviance
<span style="color:maroon">Deviance</span> refers to the distance between our fit and the data. You generally want to minimize it. 

$$Dev(\beta) = -2 log(L(\beta|data))+C$$
We can ignore C when comparing models estimated on the same data and with similar structures.

Deviance is a measure of fit that is similar to the residual sum of squares, for a broader class of models (logistic regression, etc). 

We'll think about deviance as a cost to be minimized. 

<span style="color:maroon">Minimize Deviance $\iff$ Maximize likelihood</span> 

## Bootstrap

Each observation in our data was independently sampled from some distribution. 
What if every time we wanted a new sample, we just put all our observations in an urn, drew our $n$ of them (with replacement), or $n-10$ (without replacement) and calculated the parameter in that sample? 

We could do this ten-thousand times. And have a theory-free distribution of possible values our sample could have taken. 

This is the simplest form of the bootstrap. And it is powerful.

- Can be used for model aggregation, or for inference.

## OOS

We care about how well our model works **out-of-sample**.

One way to test this is to use a "validation sample".

1. Randomly split your data into two samples
    - usually named "testing" and "training".
2. Fit your model using the "training" data
3. Test predictions on the left-out "testing" data.


# Week 3: AIC, Stepwise Regression, LASSO, CV (K-fold)

## AIC


We can (with some assumptions) estimate OOS deviance using Akaike's Information Criterion. For a model $M$ with $k$ variables estimated to be $\hat{\beta}_M$

$$ AIC = 2k + Dev(\hat{\beta}_M) = 2k - 2 log(L(\hat{\beta}_M|data))$$


## OOS performance

In sample, adding variables always improves predictive accuracy.

- IS $R^2$ for full model (200 vars): 56%
- IS $R^2$ for FDR cut model (25 vars): 18%

Out of Sample, we may gain predictive accuracy by dropping variables. 

- OOS $R^2$ for the full model: <span style="color:maroon">-5.87.</span> 
- OOS $R^2$ for the cut model: 0.10.


**THIS IS NEGATIVE.** Out-of-Sample, we would be much *BETTER* off predicting the mean than with the full model. How do we choose variables to use?

## Stepwise Regression


There are too many possible subsets of our variables to compare all of them. So we need some other method for coming up with a subset to compare. 

Stepwise regression does this. It starts with a simple model, and "greedily" adds the best new variable repeatedly until adding a new variable no longer improves the AIC. 

Because each choice depends on the current model parameters, small changes to the data can have big consequences for our model choices. This instability is bad for our OOS prediction. 



## LASSO

LASSO is the most commonly used regularized (or penalized) regression model. The lasso penalty is the $l_1$ norm of our parameters: $pen(\beta) = \sum |\beta_j|$, which penalizes larger coefficients and non-zero coefficients. So our estimates are:
<span style="color:maroon">
$$\hat{\beta}_\lambda = \underset{\beta}{\text{argmin}}\left( Dev(\beta)+\lambda \sum_{j=1}^p |\beta_j|\right)$$</span> 

## LASSO 

```{r, echo=F}
beta = seq(-2,2,length.out=101)
penalty = abs(beta)
deviance = 2/9*(beta-1)^2
total = 0.5*(penalty+deviance)
df = data.frame(beta=rep(beta,3),value=c(penalty,deviance,total),element = as.factor(c(rep("penalty",101),rep("deviance",101),rep("scaled sum",101))))
point = which.min(total)
df2 = data.frame(beta=beta[point],value=total[point],element="scaled sum")
ggplot(df,aes(x=beta,y=value,col=element)) + 
  geom_line(lwd=1.3) + 
  facet_grid(.~element)+
  theme(legend.position = 'none')+
  geom_point(data=df2,col=2)+
  ggtitle("lambda=1")+
  xlab("beta_i")
```

## LASSO

```{r, echo=F}
beta = seq(-2,2,length.out=101)
penalty = abs(beta)
deviance = 2/9*(beta-1)^2
gamma = 0.2
total = gamma*penalty+(1-gamma)*deviance
df = data.frame(beta=rep(beta,3),value=c(penalty,deviance,total),element = as.factor(c(rep("penalty",101),rep("deviance",101),rep("scaled sum",101))))
point = which.min(total)
df2 = data.frame(beta=beta[point],value=total[point],element="scaled sum")
ggplot(df,aes(x=beta,y=value,col=element)) + 
  geom_line(lwd=1.3) + 
  facet_grid(.~element)+
  theme(legend.position = 'none')+
  geom_point(data=df2,col=2)+
  ggtitle("lambda=0.25")+
  xlab("beta_i")
```

## LASSO

```{r, echo=F}
beta = seq(-2,2,length.out=101)
penalty = abs(beta)
deviance = 2/9*(beta-1)^2
gamma = 1/11
total = gamma*penalty+(1-gamma)*deviance
df = data.frame(beta=rep(beta,3),value=c(penalty,deviance,total),element = as.factor(c(rep("penalty",101),rep("deviance",101),rep("scaled sum",101))))
point = which.min(total)
df2 = data.frame(beta=beta[point],value=total[point],element="scaled sum")
ggplot(df,aes(x=beta,y=value,col=element)) + 
  geom_line(lwd=1.3) + 
  facet_grid(.~element)+
  theme(legend.position = 'none')+
  geom_point(data=df2,col=2)+
  ggtitle("lambda=0.1")+
  xlab("beta_i")
```

## K-fold Cross-validation

LASSO gives us estimates conditional on some penalty lambda. How do we pick lambda? OOS performance. 

We will "fold" the data -- i.e. we will partition it into $K$ different groups of observations. Then, `for k=1:K`

- Use observations in group $k$ as test data.
- Train the models on the remaining data (for every $\lambda$)
- Predict the observations in group $k$ using those models
- Record the prediction errors for each lambda

## Choosing K

There are several options:

- Leave-one-out Cross-validation: AKA $K=n$ is great, but much slower (fits every model under consideration $n$ times)
- $K=5$ corresponds to 5 different 20% leave-out samples.
- $K=20$ corresponds to 20 different 5% leave-out samples.

Most people set $K \in [5,20]$. I'll mostly use 10.

<span style="color:lightblue">$\implies$ Optimizing $K$ is very 3rd order. Not worth worrying about too much beyond time considerations and some preference for larger $K$. </span> 

# Week 4: KNN, Classification, ROC, Multinomial

## KNN Basics

**Basic Idea**: Estimate $P[y|x]$ locally using the labels of similar observations in the training data. 

KNN: What is the most common class near $x^{new}$? 

1. Take the $K$ nearest neighbors $x_{i,1},...,x_{i,K}$ of $x^{new}$ in the training data
    - Nearness is (usually) Euclidean distance: $\sqrt{\sum_{j=1}^p (x^{new}_j-x_{i,k,j})^2}$
2. Estimate $P[y=j|x] = \frac1n \sum_{i=1}^K 1(y_i=j)$
3. Select the class $j$ with the highest probability.

## KNN Example Data

```{r, echo=F}
n = 50
set.seed(100)
set.seed(101)
x1 = runif(n)
x2 = runif(n)
prob  = ifelse(x1 < 0.5 & x1 > 0.25 & x2 > 0.25 & x2<0.75,0.8,0.3)
newx = data.frame(y="New Observation",x1=0.7,x2=0.6)
y  = as.factor(rbinom(n,1,prob))
levels(y) = c("1","2","New Observation")
df = data.frame(y=y,x1=x1,x2=x2)
df = rbind(df,newx)
```

```{r,echo=F}
ggplot(df,aes(x=x1,y=x2,col=y)) +
  geom_point()+
  theme(aspect.ratio = 1)
```

## KNN Example $K=7$

```{r,echo=F}
dists = sqrt((x1-0.7)^2+(x2-0.6)^2)
k7 = sort(dists)[7]
gg_circle <- function(r, xc, yc, color="black", fill=NA, ...) {
    x <- xc + r*cos(seq(0, pi, length.out=100))
    ymax <- yc + r*sin(seq(0, pi, length.out=100))
    ymin <- yc + r*sin(seq(0, -pi, length.out=100))
    annotate("ribbon", x=x, ymin=ymin, ymax=ymax, color=color, fill=fill, ...)
}
ggplot(df,aes(x=x1,y=x2,col=y))+
  gg_circle(k7,0.7,0.6,fill="green",alpha=0.3,lwd=0)+
  geom_point()+
  theme(aspect.ratio = 1)
```
The relative 'vote counts' are a very crude estimate of probability.

## KNN Example

```{r,echo=F}
grid.fineness = 81
sequence = seq(0,1,length.out=grid.fineness)
grid = expand.grid(sequence,sequence)
knn_pred = function(x,k=5) {
  dists = sqrt((x1-x[1])^2+(x2-x[2])^2) #Find all distances to current obs
  bound = sort(dists)[k]                #Find kth smallest distance
  indices = which(dists <= bound)       #Find which obs have dists 1:k
  outcomes = as.integer(y[indices])     #Find corresponding outcomes y
  round(mean(outcomes)) #Taking advantage of 2 outcomes. 
}
colnames(grid) = c("x1","x2")
yhat = as.factor(apply(grid,1,knn_pred))
levels(yhat) = c(levels(yhat),"New Observation")
df_grid = data.frame(x1=grid$x1,x2=grid$x2,y=yhat)
ggplot(df,aes(x=x1,y=x2,col=y))+
  geom_point(data=df_grid,aes(x=x1,y=x2,col=y),size=0.8)+
  theme(aspect.ratio=1)
```


## Binary Classification

Many problems can be reduced to binary classification (as above). 

KNNs are a useful non-parametric classification tool.

*Logits* are a useful parametric classification tool.  
- Remember Spam?

- Logits yield parametric decision boundaries. Easy to interpret.
- Logits are global methods. Use *all* the training data to inform predictions.
    - The probability estimates are more useful and more stable.
- Logits can do variable selection.

```{r, echo=F, message=F}
library(Matrix)
## read data and create some `interesting' variables
credit = read.csv("~/Github/bigdata/lectures/l7/credit/credit.csv")
## re-level the credit history and checking account status
credit$history = factor(credit$history, levels=c("A30","A31","A32","A33","A34"))
levels(credit$history) = c("good","good","poor","poor","terrible")
## a few others

credit$foreign = factor(credit$foreign, levels=c("A201","A202"), labels=c("foreign","german"))
credit$rent = factor(credit$housing=="A151")
credit$purpose = factor(credit$purpose, levels=c("A40","A41","A42","A43","A44","A45","A46","A47","A48","A49","A410"))
levels(credit$purpose) = c("newcar","usedcar",rep("goods/repair",4),"edu",NA,"edu","biz","biz")

xnaref = function(x){
	if(is.factor(x))
		if(!is.na(levels(x)[1]))
			x = factor(x,levels=c(NA,levels(x)),exclude=NULL)
	x
}
naref = function(DF){
	if(is.null(dim(DF))) return(xnaref(DF))
	if(!is.data.frame(DF)) 
		stop("You need to give me a data.frame or a factor")
	DF = lapply(DF, xnaref)
	return(as.data.frame(DF))
}

credit = naref(credit)
credx = sparse.model.matrix( ~ (duration + amount +
    installment + age + history +
    purpose + foreign + rent)^2, data=credit)
default = credit$Default
library(glmnet)
credscore = cv.glmnet(credx, default, family="binomial")

credx2 = model.matrix( ~ (duration + amount +
    installment + age + history +
    purpose + foreign + rent)^2, data=credit)
credit_mod = glmnet(credx2,default,family="binomial")
```

## Sensitivity and Specificity

But we may also want to think about sensitivity and specificity.

- Sensitivity: proportion of true $y=1$ classified as such.
- Specificity: proportion of true $y=0$ classified as such.

A rule is sensitive if it mostly gets the 1s right. A rule is specific if it mostly gets the 0s right.

## ROC Curve
ROC curve measures the tradeoff between sensitivity and specificity.

We can plot the ROC curve for different choices of threshold. 

```{r, echo=F}
pred = predict(credscore, credx, type="response")
pred = drop(pred) 

roc = function(p,y, ...){
  y = factor(y)
  n = length(p)
  p = as.vector(p)
  probs = seq(0,1,length=1001)
  Q = p > matrix(rep(probs,n),ncol=length(probs),byrow=TRUE)
  specificity = colMeans(!Q[y==levels(y)[1],])
  sensitivity = colMeans(Q[y==levels(y)[2],])
  plot(1-specificity, sensitivity, type="l", ...)
  abline(a=0,b=1,lty=2,col=8)
  inds = c(251,501,751)
  points(1-specificity[inds],sensitivity[inds],col=2,cex=3)
}

roc(p=pred, y=default, bty="n")
```


## AUC

The AUC is a common metric for choosing between classifiers, which selects the classifier that maximizes the Area Under the Curve -- specifically the area under the ROC curve.

```{r,echo=F}
roc(p=pred,y=default,bty="n")
```


## Multinomial

What if we have more than one class possible?
E.g. Not just $\{0,1\}$ but $\{0,1,2,...,M\}$

KNN just predicts most likely class.

Multinomial Logit builds a logit for the probability of being in each class, then rescales predictions so that the sum of probabilities is 1. (Combining these models)

# Week 5: Trees, Bagging, Ensembles, Forests

## Trees

Trees try to find groupings that are good for making predictions.

They do this by recursively partitioning groups into two. 

At each step, the tree find the partition that best improves its predictions, and makes that partition.

## Trees

```{r,echo=F, message=F}
n = 500
set.seed(101)
x1 = runif(n)
x2 = runif(n)
prob  = ifelse(x1 < 0.5 & x1 > 0.25 & x2 > 0.25 & x2<0.75,0.8,0.3)
y  = as.factor(rbinom(n,1,prob))
df = data.frame(y=y,x1=x1,x2=x2)
library(rpart)
mod = rpart(y~x1+x2,data=as.data.frame(df),cp=0)
#smod = summary(mod)
splits = c(0.033,0.032,0.019,0.01,0.002,0)
par(xpd=T)
plot(prune(mod,splits[1]))
#text(mod,cex=3,use.n=T)
```



## Trees

```{r,echo=F}
library(parttree)
#mod = rpart(y~x1+x2,data=as.data.frame(df),maxdepth=3)
ggplot(df,aes(x=x1,y=x2,col=y)) + geom_point()+ 
  geom_parttree(data = prune(mod,splits[1]),aes(fill=y),col=1, alpha = 0.2,flipaxes=T,lwd=2)
```

## Trees Problems

- Overfitting
    - How many leaves? We need to choose. 
- Very unstable. 
    - **Slight Peturbations** of the data can change the entire tree structure

One solution? Bootstrap-aggregating ("BAgging")

## Bagging: In Brief

We will boostrap the data:

- Recall: this means drawing another similar size sample with replacement and building the model with that sample -- repeatedly.

This in essence creates numerous small "peturbations" of the data. 

And it will let us create 1000 very similar models. Each of which is potentially very overfit.

## Example
```{r}
resampled_mod = function(x) {
  ind = sample(nrow(df),,replace=T)
  rpart(y~x1+x2,data=df[ind,],cp=0)
}

mod =  rpart(y~x1+x2,data=df,cp=0)
modb1 = resampled_mod(1)
modb2 = resampled_mod(1)
modb3 = resampled_mod(1)
modb4 = resampled_mod(1)
modb5 = resampled_mod(1)
modb6 = resampled_mod(1)
modb7 = resampled_mod(1)
```


## Ensembles -- The bare bones ensemble

An ensemble is some combination of smaller models which (hopefully) improves our overall predictions.

The simplest ensemble takes an average across each of the submodels predictions. 

Any time we want a prediction, we average our predicted probabilities across each model type. 

## Averaged across 7 models

```{r,echo=F}
pred_helper = function(x,xdata=grid) predict(x,newdata=xdata)[,2]
l1 = list(mod,modb1,modb2,modb3,modb4,modb5,modb6,modb7)
preds = sapply(l1,pred_helper)
preds = rowMeans(preds)
grid$preds = preds
ggplot(grid,aes(x=x1,y=x2,col=preds))+geom_point(size=3)
```


## Random Forests

Forests take the notion of Bagging, and make some minor improvements -- mostly in the name of speed.

By introducing variation into the variables under consideration, they create _even more instability_ between trees. 

But it turns out, because we are averaging across our trees, this leads to improvements in predictive power. 

_They search across a wider range of models_. 

# Week 6: Boosting, More Ensembles, Rolling Block CV


## Ensembles 2

What other types of ensembles can we build?

- More sophisticated averages:
  - inverse variance weights -- using OOS MSE to estimate prediction quality
- We could just take the predictions of 10 models and build a model (e.g. linear regression) the output on those predictions.
  - Or build a forest on them. etc.
  
## Weighted -- Example

Suppose I have two models: 
- $M_1$ always predicts $\hat y_1  = M_1(X) = \bar y$
- $M_2$ uses $p$ variables and linear regression to predict $\hat y_2 = M_2(X) = \hat \beta_0 + \hat \beta_1 x_1 +...+ \hat \beta_p x_p$

These are not equivalently good. $M_2$ is _likely_ better (not always -- see overfitting again). 

We could plug them both into our naive average, and get a prediction.


## Inverse Variance Weights

The basic idea is that a single measure of the prediction errors of each model can help us generate the best weights.

The models with the smallest average errors should get the most weight, and the models with the largest average errors should get the least weight. 

If you are minimizing MSE, this will look like weights which are proportional to the ``precision" (aka the inverse of the variance). 

## Boosting

1. Start with a class of models $F$ (e.g. tree, glm, etc). And weights $w_i = \frac1n$
2. Fit a model $M_k$ in that class using those weights.
3. Find the prediction error for each observation from that model.
4. Increase the weights for observations where predictions were most wrong, decrease the weights for observations where predictions were most correct.
5. Repeat steps 2-4 until you've built $K$ models.

Model $M_K$ will be using weights based on how poorly each previous model did at predicting each observation. 

The ensemble model is just the average across models $1:K$


## Rolling Block CV

Standard Cross-validation assumes independence between observations. What if that isn't true?

Suppose you have a model for predicting a week into the future, which uses data from the past month. 

We can estimate that model based on days 1-30, and see how wrong its forecast for day 37 is.

And then again for days 2-31 and day 38. And so forth.

This will let us use real OOS predictions for doing model selection and development. 


# Week 7: Cleaning Data: pivot, join, aggregate, create, winsorizing

## Data Cleaning 101

Rules of Thumb:

1. Keep looking at the data
2. Make small changes. 
3. Test your changes before you overwrite variables.
4. Don't overwrite actual files unless you're certain.
5. **Don't throw away potentially useful data.**

## Winsorizing

Set some threshold quantile -- e.g. 99%.

Set values above that quantile to that quantile -- squashing down the big outliers.

- Strikes a balance between keeping and removing outlier points.

# Week 8: RCTs, ATEs, CATEs, Targeting


## RCTs

![](../l15/dag1context.png)

## RCTs

![](../l15/dag2.png)


## Similarity

Typically the ATE is:

$$ \widehat{ATE} = \bar y_1 - \bar y_0 $$

But we could also define it as the average across individual treatment effects. 

$$\widetilde{ATE} = \frac1n \sum_{i=1}^n \widehat{TE}_i$$

With a lot of rewriting of sums -- we can show that when our individual treatment effects use a constant mean to predict counterfactuals:

$$ \widetilde{ATE} = \widehat{ATE}$$

## Review: TEs

An average treatment effect (ATE) is the average difference between the potential outcome under treatment, and the potential outcome under control. 

Individual treatment effects are the _individual's_ difference between potential outcomes under treatment and control.

Conditional Average Treatment Effects, are the average difference btween potential outcome under treatment and control _for some subgroup_

## Review: Targeting

Targeting mostly consists of trying to identify groups with notable (large, negative, etc) CATEs.

Many methods. We've looked at "T-learners" -- which make predictions about individual outcomes under treatment and control, then estimate differences. 

With estimated treatment effects for individuals, we can decide who to _target_. 

This needs solid out-of-sample performance to be useful. 


# Week 9: Neural nets, SGD, Optimization, Review

## Neural Nets - Essence

![](../l17/Neural Net Basics.png){height=90%}

## Gradient Descent - Essence

```{r,echo=F}
x = seq(-10,10,length.out=1000)
y = 2*x^2 -x*3+2
df = data.frame(x=x,y=y)
ggplot(df,aes(x,y))+geom_line()+
    xlab("Parameter to Optimize")+
    ylab("Loss")+
  theme(axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        axis.text.y = element_blank(),
        axis.ticks.y = element_blank())+
    annotate("segment",x=x[10],y=y[10],
            xend=x[50],yend=y[10],col=2)+
    annotate("segment",x=x[50],y=y[10],
             xend=x[50],yend=y[50],col=2)+
    annotate("segment",x=x[50],y=y[50],
            xend=x[100],yend=y[50],col=2)+
    annotate("segment",x=x[100],y=y[50],
             xend=x[100],yend=y[100],col=2)+
    annotate("segment",x=x[100],y=y[100],
            xend=x[150],yend=y[100],col=2)+
    annotate("segment",x=x[150],y=y[100],
             xend=x[150],yend=y[150],col=2)+
    annotate("segment",x=x[150],y=y[150],
            xend=x[200],yend=y[150],col=2)+
    annotate("segment",x=x[200],y=y[150],
             xend=x[200],yend=y[200],col=2)
```


# Wrap up

Thank you all. 









