mirror of
https://github.com/wassname/discovering_latent_knowledge.git
synced 2026-09-09 11:21:22 +08:00
VAE exp
This commit is contained in:
@@ -71,7 +71,15 @@ pred_last_choice_is_true = y / (y_pred2-y_pred)
|
||||
pred_last_choice_is_true # [1, -1]
|
||||
```
|
||||
|
||||
|
||||
# Running
|
||||
|
||||
```sh
|
||||
python notebooks/make_dataset2.py --max_examples 1720 220 --datasets imdb glue:qnli super_glue:boolq
|
||||
```
|
||||
|
||||
# Description
|
||||
|
||||
There is some previous work on this ([ELK](https://github.com/EleutherAI/elk), [DLK](https://github.com/collin-burns/discovering_latent_knowledge/blob/main/CCS.ipynb), CSS, etc) that all take varias approaches. They have this in common:
|
||||
|
||||
- Show the model 2 statements “the sky is blue” “the sky is green”
|
||||
|
||||
+118
@@ -1971,3 +1971,121 @@ https://colab.research.google.com/drive/1rPy82rL3iZzy2_Rd3F82RwFhlVnnroIh?usp=sh
|
||||
|
||||
|
||||
n_instances - remove this, another batch dim
|
||||
|
||||
|
||||
- nice timeseries 1d VAE from merlion https://github.dev/salesforce/Merlion/blob/01c3fc3406ebf19798cedcddbe829ae5339e1424/merlion/models/anomaly/vae.py#L186
|
||||
- simple one https://github.com/ctallec/world-models/blob/master/models/vae.py
|
||||
- neels https://github.dev/neelnanda-io/1L-Sparse-Autoencoder/blob/bcae01328a2f41d24bd4a9160828f2fc22737f75/utils.py#L106
|
||||
|
||||
|
||||
# 2023-12-08 10:01:13
|
||||
|
||||
Initial obs
|
||||
- it can still overfit on the latent state, hmmm! Well maybe I need to make it very small or space or quantized (dreamer style)
|
||||
- a tanh seems to help!... just in the mse one it makes it worse... hmm.
|
||||
|
||||
|
||||
oh CVAE generalizes well notebooks/033_train_cvae.ipynb
|
||||
test/acc 0.6785010099411011 │ 0.7179487347602844
|
||||
llm gave did didn't
|
||||
instructed to
|
||||
tell a truth 0.74 NaN
|
||||
tell a lie 0.91 0.54
|
||||
oos/acc │ 0.7257769703865051 │ 0.7664233446121216 │ 0.7627736926078796
|
||||
llm gave did didn't
|
||||
instructed to
|
||||
tell a truth 0.78 NaN
|
||||
tell a lie 1.00 0.66
|
||||
|
||||
# 2023-12-08 15:44:03
|
||||
|
||||
Questions:
|
||||
- [ ] hmm in the anthorpic [paper](https://transformer-circuits.pub/2022/toy_model/index.html#demonstrating-setup-loss) they weight by feature importance, this seems important
|
||||
- [x] anything else I need to know from the sparse transformer AE's?
|
||||
- [x] what is dictionary learning? it seems to just be a huge 1 layer sparse autoencoder. no categorical latent or anything
|
||||
- they seem to use weight norm on decoder, not tie weights. have 8 times the latent space compared to activations
|
||||
- oh they replace activations with reconstructed
|
||||
- oh actual training [tips](https://docs.google.com/document/u/0/d/187jfZSbhRjjQaazjYlThBsKp3Q0Pw3VdIHVST9H2dvw/mobilebasic)
|
||||
- what's the decoder weight norm??
|
||||
- [x] do I need something special to make it sparse? no it looks like it's just the l1 loss
|
||||
- [ ]
|
||||
- [ ] what where the dreamer learnings?
|
||||
- two-hot latent space?? I guess that means it turns into [2, 1, 0, 1]. I'm assuming neg vs pos?
|
||||
- symlog scaling for rewards prediction - I probobly don't need this
|
||||
- how do the discrete states work? https://github.dev/Eclectic-Sheep/sheeprl/blob/52f49be5971c5753e18bdf328d3035334fe688f1/sheeprl/algos/dreamer_v3/agent.py#L31
|
||||
- [ ] does my pcr probe work ok? how to debug?
|
||||
|
||||
|
||||
> Features Vary in Importance: Not all features are equally useful to a given task. Some can reduce the loss more than others. For an ImageNet model, where classifying different species of dogs is a central task, a floppy ear detector might be one of the most important features it can have. In contrast, another feature might only very slightly improve performance
|
||||
|
||||
|
||||
IRIS loss https://github.dev/eloialonso/iris/blob/ac6be401fed2b6176c9ce0cf1dc10e376c9d740d/src/models/tokenizer/tokenizer.py#L50-L55
|
||||
|
||||
# Codebook loss. Notes:
|
||||
# - beta position is different from taming and identical to original VQVAE paper
|
||||
# - VQVAE uses 0.25 by default
|
||||
beta = 1.0
|
||||
commitment_loss = (z.detach() - z_quantized).pow(2).mean() + beta * (z - z_quantized.detach()).pow(2).mean()
|
||||
reconstruction_loss = torch.abs(observations - reconstructions).mean()
|
||||
perceptual_loss = torch.mean(self.lpips(observations, reconstructions))
|
||||
|
||||
|
||||
https://openreview.net/pdf?id=o8IDoZggqO
|
||||
> We follow DreamerV3 in using discrete regression with two-hot targets and symlog scaling for rewards prediction (Bellemare et al., 2017; Imani & White,2018).
|
||||
|
||||
|
||||
|
||||
> SqrtTransform Using two-hot discrete regression with the asymmetric square root transformation intro- duced by R2D221 and used in MuZero34
|
||||
https://arxiv.org/pdf/2301.04104v1.pdf
|
||||
- R2D2 https://openreview.net/forum?id=r1lyTjAqYX
|
||||
|
||||
> The representations are sampled from a vector of softmax distributions and we take straight-through gradients through the sampling step
|
||||
- "sampled from a vector of softmax distributions"? I would like to see psudocode. I guess it just uses the distributions baked into torch fd.MultivariateNormalDiag(mean, std)
|
||||
> To train the critic, we symlog transform the targets Rλ t and then twohot encode them into a soft label for the softmax distribution produced by the critic. Twohot encoding is a generalization of onehot encoding to continuous values. It produces a vector of length |B| where all elements are 0 except for the two entries closest to the encoded continuous number, at positions k and k + 1. These two entries sum up to 1, with more weight given to the entry that is closer to the enco
|
||||
|
||||
|
||||
https://arxiv.org/pdf/2301.04104v1.pdf
|
||||
|
||||
> the world model encodes sensory inputs into a discrete representation zt t
|
||||
|
||||
cal.s.mcdougall@gmail.com
|
||||
|
||||
dictionary learning
|
||||
|
||||
- experiment I added amazon, and it doubled the training data, lets see if I get above 80% acc... I was getting ~75%
|
||||
|
||||
|
||||
https://www.alignmentforum.org/posts/F4iogK5xdNd7jDNyw/comparing-anthropic-s-dictionary-learning-to-ours
|
||||
> Size of training set: We trained our autoencoders for 10M tokens. Anthropic trained theirs for much longer, 8B tokens.
|
||||
Wow that's a lot. If I want to focus on just lying, I might need to focus on not reconstruction the whole state...
|
||||
|
||||
|
||||
## Best dreamer v3 repo?
|
||||
|
||||
|
||||
- https://github.dev/kc-ml2/SimpleDreamer oh it's dreamer 1 meh
|
||||
- https://github.dev/Eclectic-Sheep/sheeprl/blob/52f49be5971c5753e18bdf328d3035334fe688f1/sheeprl/algos/dreamer_v3/agent.py#L31
|
||||
|
||||
|
||||
symlog is simple `torch.sign(x) * torch.log(1 + torch.abs(x))`
|
||||
|
||||
# 2023-12-09 11:34:11
|
||||
|
||||
Questions:
|
||||
- understand HALOs https://twitter.com/ethayarajh/status/1732837520784957476 https://github.com/ContextualAI/HALOs
|
||||
- so it's just DPO with a differen't activation function on the reward, and notably it can use reward text instead of ranked pairs, letting you skip SFT. In a way it's just SFT?
|
||||
- [ ] discrete states, just look up QVAE?
|
||||
- [ ] hmm some use a categorical, and the gumbel reparam trick for end to end backprop
|
||||
- [ ] some use VQ-VAE which I haven't looked at before but look promising. But I want to!
|
||||
- [ ] does my pcr probe work ok? how to debug? I guess I need to check acc from it for a start
|
||||
- [ ] does my conv vae work? maybe I need transposed conv blocks?
|
||||
- perhaps just use https://github.com/ctallec/world-models/blob/master/models/vae.py#L10
|
||||
- perhaps I need to focus on important features? Or on a task?
|
||||
- e.g. if doing inference on the reconstructed parts, can I get the same output? (RAM heavy)
|
||||
- if just apply an importance multipier
|
||||
|
||||
|
||||
|
||||
```py
|
||||
QVAE psuedocode
|
||||
```
|
||||
|
||||
+2422
-596
File diff suppressed because one or more lines are too long
+49
-49
@@ -1,68 +1,68 @@
|
||||
|
||||
from tqdm.auto import tqdm
|
||||
import torch
|
||||
from torch.utils.data import DataLoader
|
||||
from datasets.arrow_dataset import Dataset
|
||||
import hashlib
|
||||
import pickle
|
||||
import numpy as np
|
||||
from typing import List, Dict, Any, Union, NewType, Optional
|
||||
# from tqdm.auto import tqdm
|
||||
# import torch
|
||||
# from torch.utils.data import DataLoader
|
||||
# from datasets.arrow_dataset import Dataset
|
||||
# import hashlib
|
||||
# import pickle
|
||||
# import numpy as np
|
||||
# from typing import List, Dict, Any, Union, NewType, Optional
|
||||
|
||||
from src.datasets.hs import ExtractHiddenStates
|
||||
from src.helpers.typing import float_to_int16, int16_to_float
|
||||
from src.helpers.ds import ds_keep_cols, clear_mem
|
||||
from src.datasets.intervene import InterventionDict
|
||||
# from src.datasets.hs import ExtractHiddenStates
|
||||
# from src.helpers.typing import float_to_int16, int16_to_float
|
||||
# from src.helpers.ds import ds_keep_cols, clear_mem
|
||||
# from src.datasets.intervene import InterventionDict
|
||||
|
||||
|
||||
def batch_hidden_states(model, tokenizer, intervention_dicts: Optional[InterventionDict], data: Dataset, batch_size=2, layer_padding=3, layer_stride=4):
|
||||
"""
|
||||
Given an encoder-decoder model, a list of data, computes the contrast hidden states on n random examples.
|
||||
Returns numpy arrays of shape (n, hidden_dim) for each candidate label, along with a boolean numpy array of shape (n,)
|
||||
with the ground truth labels
|
||||
# def batch_hidden_states(model, tokenizer, intervention_dicts: Optional[InterventionDict], data: Dataset, batch_size=2, layer_padding=3, layer_stride=4):
|
||||
# """
|
||||
# Given an encoder-decoder model, a list of data, computes the contrast hidden states on n random examples.
|
||||
# Returns numpy arrays of shape (n, hidden_dim) for each candidate label, along with a boolean numpy array of shape (n,)
|
||||
# with the ground truth labels
|
||||
|
||||
This is deliberately simple so that it's easy to understand, rather than being optimized for efficiency
|
||||
"""
|
||||
ehs = ExtractHiddenStates(model, tokenizer, intervention_dicts=intervention_dicts, layer_stride=layer_stride, layer_padding=layer_padding)
|
||||
# This is deliberately simple so that it's easy to understand, rather than being optimized for efficiency
|
||||
# """
|
||||
# ehs = ExtractHiddenStates(model, tokenizer, intervention_dicts=intervention_dicts, layer_stride=layer_stride, layer_padding=layer_padding)
|
||||
|
||||
torch_cols = ['input_ids', 'attention_mask', 'choice_ids']
|
||||
ds_t_subset = ds_keep_cols(data, torch_cols)
|
||||
ds_t_subset.set_format(type='torch')
|
||||
# torch_cols = ['input_ids', 'attention_mask', 'choice_ids']
|
||||
# ds_t_subset = ds_keep_cols(data, torch_cols)
|
||||
# ds_t_subset.set_format(type='torch')
|
||||
|
||||
ds_p_subset = data.remove_columns(torch_cols)
|
||||
# ds_p_subset = data.remove_columns(torch_cols)
|
||||
|
||||
dl = DataLoader(ds_t_subset, batch_size=batch_size, shuffle=False)
|
||||
for i, batch in enumerate(tqdm(dl, desc='get hidden states')):
|
||||
input_ids, attention_mask, choice_ids = batch["input_ids"], batch["attention_mask"], batch["choice_ids"]
|
||||
nn = len(input_ids)
|
||||
index = i*batch_size+np.arange(nn)
|
||||
# dl = DataLoader(ds_t_subset, batch_size=batch_size, shuffle=False)
|
||||
# for i, batch in enumerate(tqdm(dl, desc='get hidden states')):
|
||||
# input_ids, attention_mask, choice_ids = batch["input_ids"], batch["attention_mask"], batch["choice_ids"]
|
||||
# nn = len(input_ids)
|
||||
# index = i*batch_size+np.arange(nn)
|
||||
|
||||
# different due to dropout
|
||||
hsl = ehs.get_batch_of_hidden_states(input_ids=input_ids, attention_mask=attention_mask, choice_ids=choice_ids)
|
||||
# # different due to dropout
|
||||
# hsl = ehs.get_batch_of_hidden_states(input_ids=input_ids, attention_mask=attention_mask, choice_ids=choice_ids)
|
||||
|
||||
for j in range(nn):
|
||||
# let's add the non torch metadata like label, prompt, lie, etc
|
||||
k = i*batch_size + j
|
||||
info = ds_p_subset[k]
|
||||
# for j in range(nn):
|
||||
# # let's add the non torch metadata like label, prompt, lie, etc
|
||||
# k = i*batch_size + j
|
||||
# info = ds_p_subset[k]
|
||||
|
||||
large_arrays_keys = [k for k,v in hsl.items() if isinstance(v, torch.Tensor) and v.ndim>2]
|
||||
# large_arrays_keys = [k for k,v in hsl.items() if isinstance(v, torch.Tensor) and v.ndim>2]
|
||||
|
||||
# TODO deal with multiple lists of hs in hs0
|
||||
large_arrays = {k:hsl[k][j] for k in large_arrays_keys}
|
||||
# # TODO deal with multiple lists of hs in hs0
|
||||
# large_arrays = {k:hsl[k][j] for k in large_arrays_keys}
|
||||
|
||||
yield dict(
|
||||
# yield dict(
|
||||
|
||||
# large_arrays_keys=large_arrays_keys,
|
||||
scores0=hsl["scores"][j],
|
||||
# layer_names=hsl["layers"][j] if k==0 else [], # just in the first one, to save space
|
||||
# # large_arrays_keys=large_arrays_keys,
|
||||
# scores0=hsl["scores"][j],
|
||||
# # layer_names=hsl["layers"][j] if k==0 else [], # just in the first one, to save space
|
||||
|
||||
ds_index=index[j],
|
||||
# ds_index=index[j],
|
||||
|
||||
# int16 makes our storage much smaller
|
||||
**large_arrays,
|
||||
# # int16 makes our storage much smaller
|
||||
# **large_arrays,
|
||||
|
||||
**info
|
||||
)
|
||||
# **info
|
||||
# )
|
||||
|
||||
info = large_arrays = hsl = None
|
||||
clear_mem()
|
||||
# info = large_arrays = hsl = None
|
||||
# clear_mem()
|
||||
|
||||
|
||||
+4
-1
@@ -32,12 +32,14 @@ class imdbHSDataModule(pl.LightningDataModule):
|
||||
batch_size: int=32,
|
||||
x_cols = ['end_hidden_states'],
|
||||
skip_layers = 0,
|
||||
use_diff = True,
|
||||
):
|
||||
super().__init__()
|
||||
self.save_hyperparameters(ignore=["ds"])
|
||||
self.ds = ds
|
||||
self.x_cols = x_cols
|
||||
self.skip_layers = skip_layers
|
||||
self.use_diff = use_diff
|
||||
|
||||
def setup(self, stage: str):
|
||||
h = self.hparams
|
||||
@@ -61,7 +63,8 @@ class imdbHSDataModule(pl.LightningDataModule):
|
||||
b = len(self.ds_hs)
|
||||
# take the diff between layers. Shape batch, layers, hidden_states, inferences
|
||||
hs = torch.tensor(self.ds_hs['end_hidden_states'])
|
||||
hs = hs.diff(1, axis=1) # this makes it the residual between layers
|
||||
if self.use_diff:
|
||||
hs = hs.diff(1, axis=1) # this makes it the residual between layers
|
||||
if self.skip_layers:
|
||||
hs = hs[:, self.skip_layers:] # drop the first 10 layers to prevent overfitting?
|
||||
self.hs0 = hs[..., 0]
|
||||
|
||||
+179
-179
@@ -1,220 +1,220 @@
|
||||
from dataclasses import dataclass
|
||||
import lightning as pl
|
||||
from loguru import logger
|
||||
from transformers import (
|
||||
AutoTokenizer,
|
||||
AutoModelForSeq2SeqLM,
|
||||
AutoModelForMaskedLM,
|
||||
AutoModelForCausalLM,
|
||||
AutoConfig,
|
||||
AutoModel,
|
||||
PreTrainedTokenizer,
|
||||
PreTrainedModel
|
||||
)
|
||||
from typing import Optional, List, Tuple, Dict, NewType
|
||||
from transformers import LogitsProcessorList
|
||||
import functools
|
||||
from src.helpers.torch import to_numpy
|
||||
from src.datasets.dropout import enable_dropout
|
||||
import re
|
||||
# from dataclasses import dataclass
|
||||
# import lightning as pl
|
||||
# from loguru import logger
|
||||
# from transformers import (
|
||||
# AutoTokenizer,
|
||||
# AutoModelForSeq2SeqLM,
|
||||
# AutoModelForMaskedLM,
|
||||
# AutoModelForCausalLM,
|
||||
# AutoConfig,
|
||||
# AutoModel,
|
||||
# PreTrainedTokenizer,
|
||||
# PreTrainedModel
|
||||
# )
|
||||
# from typing import Optional, List, Tuple, Dict, NewType
|
||||
# from transformers import LogitsProcessorList
|
||||
# import functools
|
||||
# from src.helpers.torch import to_numpy
|
||||
# from src.datasets.dropout import enable_dropout
|
||||
# import re
|
||||
|
||||
from tqdm.auto import tqdm
|
||||
# from src.datasets.hs import ExtractHiddenStates
|
||||
from torch.utils.data import DataLoader
|
||||
from datasets import Dataset
|
||||
import numpy as np
|
||||
import torch
|
||||
import torch.nn.functional as F
|
||||
from baukit.nethook import Trace, TraceDict, recursive_copy
|
||||
from einops import rearrange, reduce, repeat
|
||||
from src.datasets.scores import choice2id, choice2ids
|
||||
from src.helpers.torch import clear_mem, detachcpu
|
||||
from collections import defaultdict
|
||||
from dataclasses import field
|
||||
from src.datasets.intervene import InterventionDict, intervention_meta_fn
|
||||
from functools import partial
|
||||
# from tqdm.auto import tqdm
|
||||
# # from src.datasets.hs import ExtractHiddenStates
|
||||
# from torch.utils.data import DataLoader
|
||||
# from datasets import Dataset
|
||||
# import numpy as np
|
||||
# import torch
|
||||
# import torch.nn.functional as F
|
||||
# from baukit.nethook import Trace, TraceDict, recursive_copy
|
||||
# from einops import rearrange, reduce, repeat
|
||||
# from src.datasets.scores import choice2id, choice2ids
|
||||
# from src.helpers.torch import clear_mem, detachcpu
|
||||
# from collections import defaultdict
|
||||
# from dataclasses import field
|
||||
# from src.datasets.intervene import InterventionDict, intervention_meta_fn
|
||||
# from functools import partial
|
||||
|
||||
|
||||
# def noise_for_embeds(inputs_embeds, seed=42, std = 2e-2):
|
||||
# B, S, embed_dim = inputs_embeds.shape
|
||||
# with torch.random.fork_rng(devices=[inputs_embeds.device.index]):
|
||||
# torch.manual_seed(seed)
|
||||
# noise = torch.normal(0., std, (embed_dim, ))
|
||||
# noise = repeat(noise, 't -> b s t', b=B, s=S).to(inputs_embeds.device).to(inputs_embeds.dtype)
|
||||
# return noise
|
||||
# # def noise_for_embeds(inputs_embeds, seed=42, std = 2e-2):
|
||||
# # B, S, embed_dim = inputs_embeds.shape
|
||||
# # with torch.random.fork_rng(devices=[inputs_embeds.device.index]):
|
||||
# # torch.manual_seed(seed)
|
||||
# # noise = torch.normal(0., std, (embed_dim, ))
|
||||
# # noise = repeat(noise, 't -> b s t', b=B, s=S).to(inputs_embeds.device).to(inputs_embeds.dtype)
|
||||
# # return noise
|
||||
|
||||
def tcopy(x: torch.Tensor):
|
||||
return x.clone().detach().cpu()
|
||||
# def tcopy(x: torch.Tensor):
|
||||
# return x.clone().detach().cpu()
|
||||
|
||||
def counterfactual_loss(model, scores, token_y, token_n):
|
||||
"""do a backwards pass where the loss is the distance to the opposite scores"""
|
||||
eps = 1e-4
|
||||
model.zero_grad()
|
||||
assert token_y.shape[1]<2, 'FIXME just use the first token for now'
|
||||
score_y = torch.index_select(scores, 1, token_y[:, 0])
|
||||
score_n = torch.index_select(scores, 1, token_n[:, 0])
|
||||
# this loss would be zero if the logits of the positive and negative tokens werre flipped
|
||||
loss = F.l1_loss(score_y, score_n) + F.l1_loss(score_n, score_y)
|
||||
return loss
|
||||
# def counterfactual_loss(model, scores, token_y, token_n):
|
||||
# """do a backwards pass where the loss is the distance to the opposite scores"""
|
||||
# eps = 1e-4
|
||||
# model.zero_grad()
|
||||
# assert token_y.shape[1]<2, 'FIXME just use the first token for now'
|
||||
# score_y = torch.index_select(scores, 1, token_y[:, 0])
|
||||
# score_n = torch.index_select(scores, 1, token_n[:, 0])
|
||||
# # this loss would be zero if the logits of the positive and negative tokens werre flipped
|
||||
# loss = F.l1_loss(score_y, score_n) + F.l1_loss(score_n, score_y)
|
||||
# return loss
|
||||
|
||||
|
||||
def stack_trace_returns(ret: TraceDict, names: List[str]) -> torch.Tensor:
|
||||
hs = [ret[h].output for h in names]
|
||||
hs = [h[0] if isinstance(h, tuple) else h for h in hs] # from a head it's a tuple
|
||||
return rearrange(hs, 'layers b s hs -> b layers s hs')[:, :, -1]
|
||||
|
||||
# def stack_trace_grad_returns(ret: TraceDict, names: List[str]) -> torch.Tensor:
|
||||
# hs = [ret[h].output.grad.detach() for h in names]
|
||||
# def stack_trace_returns(ret: TraceDict, names: List[str]) -> torch.Tensor:
|
||||
# hs = [ret[h].output for h in names]
|
||||
# hs = [h[0] if isinstance(h, tuple) else h for h in hs] # from a head it's a tuple
|
||||
# return rearrange(hs, 'layers b s hs -> b layers s hs')[:, :, -1]
|
||||
|
||||
# def select_weight_grads(weight_grads: Dict[str, torch.Tensor], pattern:str= ".+attn.c_proj.weight", mean_axis:int=1):
|
||||
# grads = [g.mean(mean_axis) for k,g in weight_grads.items() if re.match(pattern, k)]
|
||||
# assert len(grads), f"non of pattern='{pattern}' found in {weight_grads.keys()}"
|
||||
# return rearrange(grads, "lyrs b hs -> b lyrs hs")
|
||||
# # def stack_trace_grad_returns(ret: TraceDict, names: List[str]) -> torch.Tensor:
|
||||
# # hs = [ret[h].output.grad.detach() for h in names]
|
||||
# # return rearrange(hs, 'layers b s hs -> b layers s hs')[:, :, -1]
|
||||
|
||||
# # def select_weight_grads(weight_grads: Dict[str, torch.Tensor], pattern:str= ".+attn.c_proj.weight", mean_axis:int=1):
|
||||
# # grads = [g.mean(mean_axis) for k,g in weight_grads.items() if re.match(pattern, k)]
|
||||
# # assert len(grads), f"non of pattern='{pattern}' found in {weight_grads.keys()}"
|
||||
# # return rearrange(grads, "lyrs b hs -> b lyrs hs")
|
||||
|
||||
|
||||
|
||||
@dataclass
|
||||
class ExtractHiddenStates:
|
||||
# @dataclass
|
||||
# class ExtractHiddenStates:
|
||||
|
||||
model: PreTrainedModel
|
||||
tokenizer: PreTrainedTokenizer
|
||||
intervention_dicts: Optional[InterventionDict] = None
|
||||
layer_stride: int = 8
|
||||
layer_padding: int = 3
|
||||
# model: PreTrainedModel
|
||||
# tokenizer: PreTrainedTokenizer
|
||||
# intervention_dicts: Optional[InterventionDict] = None
|
||||
# layer_stride: int = 8
|
||||
# layer_padding: int = 3
|
||||
|
||||
def get_layer_names(self):
|
||||
# for WizardLM/WizardCoder-3B-V1.0
|
||||
# HEADS = [f"transformer.h.{i}.attn.c_proj" for i in range(self.model.config.num_hidden_layers)]
|
||||
# MLPS = [f"transformer.h.{i}.mlp" for i in range(self.model.config.num_hidden_layers)]
|
||||
# def get_layer_names(self):
|
||||
# # for WizardLM/WizardCoder-3B-V1.0
|
||||
# # HEADS = [f"transformer.h.{i}.attn.c_proj" for i in range(self.model.config.num_hidden_layers)]
|
||||
# # MLPS = [f"transformer.h.{i}.mlp" for i in range(self.model.config.num_hidden_layers)]
|
||||
|
||||
# for "WizardLM/WizardCoder-Python-13B-V1.0"
|
||||
# HACK: depends on model layout
|
||||
layers_names_h = [f"model.layers.{i}.self_attn" for i in range(self.model.config.num_hidden_layers)]
|
||||
layers_names_mlp = [f"model.layers.{i}.mlp" for i in range(self.model.config.num_hidden_layers)]
|
||||
return self.get_layer_selection(layers_names_h) + self.get_layer_selection(layers_names_mlp)
|
||||
# # for "WizardLM/WizardCoder-Python-13B-V1.0"
|
||||
# # HACK: depends on model layout
|
||||
# layers_names_h = [f"model.layers.{i}.self_attn" for i in range(self.model.config.num_hidden_layers)]
|
||||
# layers_names_mlp = [f"model.layers.{i}.mlp" for i in range(self.model.config.num_hidden_layers)]
|
||||
# return self.get_layer_selection(layers_names_h) + self.get_layer_selection(layers_names_mlp)
|
||||
|
||||
|
||||
def get_batch_of_hidden_states(
|
||||
self,
|
||||
input_text: Optional[List[str]] = None,
|
||||
input_ids: torch.Tensor = None,
|
||||
attention_mask: Optional[torch.Tensor] = None,
|
||||
choice_ids: List[torch.Tensor] = None,
|
||||
truncation_length=999,
|
||||
debug=False,
|
||||
):
|
||||
"""
|
||||
Given a decoder model and a batch of texts, gets a pair of hidden states (in a given layer) on that input texts
|
||||
# def get_batch_of_hidden_states(
|
||||
# self,
|
||||
# input_text: Optional[List[str]] = None,
|
||||
# input_ids: torch.Tensor = None,
|
||||
# attention_mask: Optional[torch.Tensor] = None,
|
||||
# choice_ids: List[torch.Tensor] = None,
|
||||
# truncation_length=999,
|
||||
# debug=False,
|
||||
# ):
|
||||
# """
|
||||
# Given a decoder model and a batch of texts, gets a pair of hidden states (in a given layer) on that input texts
|
||||
|
||||
The idea is this: given two pairs of hidden states, where everything is the same except r dropout. Then tell me which one is more truthful?
|
||||
"""
|
||||
assert (input_ids is not None) or (input_text is not None), "need to provide input_ids or input_text"
|
||||
assert self.tokenizer.truncation_side == 'left'
|
||||
# The idea is this: given two pairs of hidden states, where everything is the same except r dropout. Then tell me which one is more truthful?
|
||||
# """
|
||||
# assert (input_ids is not None) or (input_text is not None), "need to provide input_ids or input_text"
|
||||
# assert self.tokenizer.truncation_side == 'left'
|
||||
|
||||
if input_text:
|
||||
raise NotImplementedError("FIXME")
|
||||
t = self.tokenizer(
|
||||
input_text,
|
||||
return_tensors="pt",
|
||||
add_special_tokens=True,
|
||||
padding='max_length', max_length=truncation_length, truncation=True, return_attention_mask=True,
|
||||
)
|
||||
input_ids = t.input_ids.to(self.model.device)
|
||||
attention_mask = t.attention_mask.to(self.model.device)
|
||||
else:
|
||||
input_ids = input_ids.to(self.model.device)
|
||||
attention_mask = attention_mask.to(self.model.device)
|
||||
choice_ids = choice_ids.to(self.model.device)
|
||||
# if input_text:
|
||||
# raise NotImplementedError("FIXME")
|
||||
# t = self.tokenizer(
|
||||
# input_text,
|
||||
# return_tensors="pt",
|
||||
# add_special_tokens=True,
|
||||
# padding='max_length', max_length=truncation_length, truncation=True, return_attention_mask=True,
|
||||
# )
|
||||
# input_ids = t.input_ids.to(self.model.device)
|
||||
# attention_mask = t.attention_mask.to(self.model.device)
|
||||
# else:
|
||||
# input_ids = input_ids.to(self.model.device)
|
||||
# attention_mask = attention_mask.to(self.model.device)
|
||||
# choice_ids = choice_ids.to(self.model.device)
|
||||
|
||||
# forward pass
|
||||
last_token = -1
|
||||
# # forward pass
|
||||
# last_token = -1
|
||||
|
||||
layers_names = self.get_layer_names()
|
||||
# layers_names = self.get_layer_names()
|
||||
|
||||
self.model.eval()
|
||||
# self.model.eval()
|
||||
|
||||
|
||||
if self.intervention_dicts is not None:
|
||||
# extraction mode
|
||||
# 15 is a magic number from honest_llama
|
||||
intervention_fn1 = partial(intervention_meta_fn, interventions=self.intervention_dicts, alpha=-15)
|
||||
intervention_fn2 = partial(intervention_meta_fn, interventions=self.intervention_dicts, alpha=15)
|
||||
edit_outputs = [intervention_fn1, intervention_fn2]
|
||||
else:
|
||||
# calibration mode
|
||||
edit_outputs = [None]
|
||||
# if self.intervention_dicts is not None:
|
||||
# # extraction mode
|
||||
# # 15 is a magic number from honest_llama
|
||||
# intervention_fn1 = partial(intervention_meta_fn, interventions=self.intervention_dicts, alpha=-15)
|
||||
# intervention_fn2 = partial(intervention_meta_fn, interventions=self.intervention_dicts, alpha=15)
|
||||
# edit_outputs = [intervention_fn1, intervention_fn2]
|
||||
# else:
|
||||
# # calibration mode
|
||||
# edit_outputs = [None]
|
||||
|
||||
with torch.no_grad():
|
||||
multi_outs = defaultdict(list)
|
||||
for edit_output in edit_outputs:
|
||||
with TraceDict(self.model, layers_names, retain_grad=False, detach=True, edit_output=edit_output) as ret:
|
||||
model_inputs = self.model.prepare_inputs_for_generation(input_ids=input_ids, attention_mask=attention_mask, use_cache=False)
|
||||
outputs = self.model.forward(
|
||||
**model_inputs,
|
||||
return_dict=True,
|
||||
output_hidden_states=True,
|
||||
)
|
||||
outputs["scores"] = outputs.logits[:, last_token, :].float()
|
||||
# with torch.no_grad():
|
||||
# multi_outs = defaultdict(list)
|
||||
# for edit_output in edit_outputs:
|
||||
# with TraceDict(self.model, layers_names, retain_grad=False, detach=True, edit_output=edit_output) as ret:
|
||||
# model_inputs = self.model.prepare_inputs_for_generation(input_ids=input_ids, attention_mask=attention_mask, use_cache=False)
|
||||
# outputs = self.model.forward(
|
||||
# **model_inputs,
|
||||
# return_dict=True,
|
||||
# output_hidden_states=True,
|
||||
# )
|
||||
# outputs["scores"] = outputs.logits[:, last_token, :].float()
|
||||
|
||||
# stack
|
||||
hidden_states = list(outputs.hidden_states)
|
||||
hidden_states = rearrange(hidden_states, 'lyrs b seq hs -> b lyrs seq hs')[:, :, last_token]
|
||||
## from ret, we get the layer activation and the grads on them
|
||||
head_activation = tcopy(stack_trace_returns(ret, layers_names))
|
||||
# mlp_activation = tcopy(stack_trace_returns(ret, MLPS))
|
||||
# # stack
|
||||
# hidden_states = list(outputs.hidden_states)
|
||||
# hidden_states = rearrange(hidden_states, 'lyrs b seq hs -> b lyrs seq hs')[:, :, last_token]
|
||||
# ## from ret, we get the layer activation and the grads on them
|
||||
# head_activation = tcopy(stack_trace_returns(ret, layers_names))
|
||||
# # mlp_activation = tcopy(stack_trace_returns(ret, MLPS))
|
||||
|
||||
# collect outputs
|
||||
multi_outs['scores'].append(outputs["scores"])
|
||||
# multi_outs['hidden_states'].append(hidden_states)
|
||||
multi_outs['head_activation'].append(head_activation)
|
||||
# multi_outs['mlp_activation'].append(mlp_activation)
|
||||
# # collect outputs
|
||||
# multi_outs['scores'].append(outputs["scores"])
|
||||
# # multi_outs['hidden_states'].append(hidden_states)
|
||||
# multi_outs['head_activation'].append(head_activation)
|
||||
# # multi_outs['mlp_activation'].append(mlp_activation)
|
||||
|
||||
# stack
|
||||
multi_outs['scores'] = torch.stack(multi_outs['scores'], -1)
|
||||
# multi_outs['mlp_activation'] = torch.stack(multi_outs['mlp_activation'], -1)
|
||||
multi_outs['head_activation'] = torch.stack(multi_outs['head_activation'], -1)
|
||||
# # stack
|
||||
# multi_outs['scores'] = torch.stack(multi_outs['scores'], -1)
|
||||
# # multi_outs['mlp_activation'] = torch.stack(multi_outs['mlp_activation'], -1)
|
||||
# multi_outs['head_activation'] = torch.stack(multi_outs['head_activation'], -1)
|
||||
|
||||
# combine
|
||||
out_common = dict(input_ids=input_ids, attention_mask=attention_mask, layers=layers_names,)
|
||||
if debug:
|
||||
out_common['input_truncated'] = self.tokenizer.batch_decode(input_ids)
|
||||
out_common['text_ans'] = self.tokenizer.batch_decode(outputs["scores"].softmax(-1).argmax(-1))
|
||||
# # combine
|
||||
# out_common = dict(input_ids=input_ids, attention_mask=attention_mask, layers=layers_names,)
|
||||
# if debug:
|
||||
# out_common['input_truncated'] = self.tokenizer.batch_decode(input_ids)
|
||||
# out_common['text_ans'] = self.tokenizer.batch_decode(outputs["scores"].softmax(-1).argmax(-1))
|
||||
|
||||
out = {**multi_outs, **out_common}
|
||||
# out = {**multi_outs, **out_common}
|
||||
|
||||
# detach
|
||||
out = {k: detachcpu(v) for k, v in out.items()}
|
||||
# # detach
|
||||
# out = {k: detachcpu(v) for k, v in out.items()}
|
||||
|
||||
|
||||
# I shouldn't have to do this but I get memory leaks
|
||||
outputs = hidden_states = hidden_states2 = loss = orig_state_dict = scores = token_y = token_n = input_ids = attention_mask = choice_ids = residual_stream = residual_stream2 = None
|
||||
clear_mem()
|
||||
return out
|
||||
# # I shouldn't have to do this but I get memory leaks
|
||||
# outputs = hidden_states = hidden_states2 = loss = orig_state_dict = scores = token_y = token_n = input_ids = attention_mask = choice_ids = residual_stream = residual_stream2 = None
|
||||
# clear_mem()
|
||||
# return out
|
||||
|
||||
|
||||
|
||||
def get_layer_selection(self, layer_names):
|
||||
"""Sometimes we don't want to save all layers.
|
||||
# def get_layer_selection(self, layer_names):
|
||||
# """Sometimes we don't want to save all layers.
|
||||
|
||||
We skip the first few (data leakage?). Stride the the middle (could be valuable), and include the last few (possibly high level concepts).
|
||||
# We skip the first few (data leakage?). Stride the the middle (could be valuable), and include the last few (possibly high level concepts).
|
||||
|
||||
See also https://www.lesswrong.com/posts/bWxNPMy5MhPnQTzKz/what-discovering-latent-knowledge-did-and-did-not-find-4
|
||||
"""
|
||||
module_names = [k for k,v in self.model.named_modules()]
|
||||
layers_not_found = set(layer_names)-set(module_names)
|
||||
assert len(layers_not_found)==0, f"some layers not found in model: {layers_not_found}. we have {layer_names}"
|
||||
# See also https://www.lesswrong.com/posts/bWxNPMy5MhPnQTzKz/what-discovering-latent-knowledge-did-and-did-not-find-4
|
||||
# """
|
||||
# module_names = [k for k,v in self.model.named_modules()]
|
||||
# layers_not_found = set(layer_names)-set(module_names)
|
||||
# assert len(layers_not_found)==0, f"some layers not found in model: {layers_not_found}. we have {layer_names}"
|
||||
|
||||
# for self.layer_padding, skip the first few
|
||||
num_layers = len(layer_names)-1
|
||||
strided_layers = torch.arange(
|
||||
self.layer_padding,
|
||||
num_layers-self.layer_padding,
|
||||
self.layer_stride,
|
||||
).tolist()
|
||||
# for self.layer_padding ALWAYS include the last few. Why, this is based on the intuition that the last layers may be the most valuable
|
||||
last_few = torch.arange(num_layers-self.layer_padding, num_layers).tolist()
|
||||
layers_inds = sorted(set(list(strided_layers)+list(last_few)))
|
||||
return [layer_names[i] for i in layers_inds]
|
||||
# # for self.layer_padding, skip the first few
|
||||
# num_layers = len(layer_names)-1
|
||||
# strided_layers = torch.arange(
|
||||
# self.layer_padding,
|
||||
# num_layers-self.layer_padding,
|
||||
# self.layer_stride,
|
||||
# ).tolist()
|
||||
# # for self.layer_padding ALWAYS include the last few. Why, this is based on the intuition that the last layers may be the most valuable
|
||||
# last_few = torch.arange(num_layers-self.layer_padding, num_layers).tolist()
|
||||
# layers_inds = sorted(set(list(strided_layers)+list(last_few)))
|
||||
# return [layer_names[i] for i in layers_inds]
|
||||
|
||||
|
||||
|
||||
@@ -67,6 +67,7 @@ def create_cache_interventions(model, tokenizer, cfg, N_fit_examples=20, batch_s
|
||||
|
||||
train_labels = np.array(dataset_fit['label_true'])
|
||||
if get_negative:
|
||||
# FIXME: does this work with PCA, since it's directionless
|
||||
train_labels = -1 * train_labels
|
||||
rep_reading_pipeline = pipeline("rep-reading", model=model, tokenizer=tokenizer)
|
||||
honesty_rep_reader = rep_reading_pipeline.get_directions(
|
||||
|
||||
@@ -163,7 +163,8 @@ class PLConvProbeLinear(PLRankingBase):
|
||||
self.head = nn.Sequential(
|
||||
LinBnDrop(n, n),
|
||||
LinBnDrop(n, n),
|
||||
nn.Linear(n, 1),
|
||||
nn.Linear(n, 1),
|
||||
# nn.Tanh(),
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
|
||||
@@ -87,7 +87,8 @@ class RepControlPipeline2(FeatureExtractionPipeline):
|
||||
assert inputs['input_ids'].ndim == 2, f"expected input_ids to be (batch, seq), got {inputs['input_ids'].shape}"
|
||||
|
||||
# make intervention functions
|
||||
layers_names = [self.layer_name_tmpl.format(i) for i in activations[0].keys()]
|
||||
layers_names = [self.layer_name_tmpl.format(i) for i in activations[0].keys()]
|
||||
# FIXME: [0] is positive, [1] is negative. We can also multiply by -1, 0, or 1
|
||||
activations_pos_i = Activations({self.layer_name_tmpl.format(k):v for k,v in activations[1].items()})
|
||||
activations_neut = Activations({self.layer_name_tmpl.format(k):0. * v for k,v in activations[0].items()})
|
||||
edit_fn_pos = partial(intervention_meta_fn2, activations=activations_pos_i)
|
||||
|
||||
@@ -140,6 +140,7 @@ class RepReadingPipeline(Pipeline):
|
||||
relative_hidden_states = {k: np.copy(v) for k, v in hidden_states.items()}
|
||||
for layer in hidden_layers:
|
||||
for _ in range(n_difference):
|
||||
# TODO: check this, it's even - odd? on what dimension?
|
||||
relative_hidden_states[layer] = relative_hidden_states[layer][::2] - relative_hidden_states[layer][1::2]
|
||||
|
||||
# get the directions
|
||||
|
||||
Reference in New Issue
Block a user