Mapa da aula
Aula 25 publicada. Mapa, exemplo, EX073.
flowchart TD
N0["25.1 Regras do laboratório"]
N1["25.2 Experimentos"]
N2["25.3 Interpretação"]
N3["25.4 Relatório de laboratório"]
N4["25.4b Alteração de parâmetros"]
N5["25.4c Segurança do código de laboratório"]
N6["Síntese"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6
N6 --> EX["Exemplo resolvido"]
EX --> Q["Exercício EX073"]
Desenho lógico
flowchart LR
N0["25.1 Regras do laboratório"]
N1["25.2 Experimentos"]
N2["25.3 Interpretação"]
N3["25.4 Relatório de laboratório"]
N4["25.4b Alteração de parâmetros"]
N5["25.4c Segurança do código de laboratório"]
N6["Síntese"]
N0 --> N1 --> N2 --> N3 --> N4 --> N5 --> N6 --> Q["EX073"]
[25.1 Regras do laboratório]
|
v
[25.2 Experimentos]
|
v
[25.3 Interpretação]
|
v
[25.4 Relatório de laboratório]
|
v
[25.4b Alteração de parâmetros]
|
v
[25.4c Segurança do código de laboratório]
|
v
[Síntese]
|
v
[Exemplo resolvido] --> [Exercício EX073]
Objetivos de aprendizagem
Você vai (1) montar um laboratório de segurança de IA seguro e isolado; (2) executar experimentos de evasão, envenenamento, privacidade e injeção em modelos de brinquedo; (3) registrar os resultados de forma reprodutível; e (4) escrever um relatório de laboratório curto.
25.1 Regras do laboratório
Use ambientes isolados, dados sintéticos e payloads inofensivos (por exemplo, endereços no domínio reservado .invalid). Nunca aponte ferramentas do laboratório para serviços reais. Registre o que executou, com versões e sementes. O domínio reservado importa porque um payload que envia dados para um endereço real poderia vazar informações mesmo num teste. Registrar versões e sementes permite que Rina reproduza depois exatamente a mesma tabela e que sua orientadora verifique que os números do relatório vieram do código entregue, e não de uma edição anterior.
25.2 Experimentos
Execute os quatro laboratórios e registre suas saídas:
"""Lab E: evasion of a toy linear classifier (synthetic data, stdlib only).
Trains logistic regression on two synthetic clusters, then applies a
fast-gradient-sign (FGSM-style) perturbation of size eps to every test point
and reports clean vs adversarial accuracy. Teaching model only."""
import math, random
rng = random.Random(7)
def point(label):
cx = 1.0 if label else -1.0
return [rng.gauss(cx, 0.7), rng.gauss(cx, 0.7)], label
train = [point(i % 2) for i in range(400)]
test = [point(i % 2) for i in range(200)]
w, b = [0.0, 0.0], 0.0
sig = lambda z: 1 / (1 + math.exp(-z))
for epoch in range(200):
gw, gb = [0.0, 0.0], 0.0
for x, y in train:
p = sig(w[0]*x[0] + w[1]*x[1] + b)
gw[0] += (p - y)*x[0]; gw[1] += (p - y)*x[1]; gb += p - y
w = [w[i] - 0.1*gw[i]/len(train) for i in range(2)]; b -= 0.1*gb/len(train)
def acc(data, eps):
ok = 0
for x, y in data:
# gradient of the loss w.r.t. x is (p - y) * w; FGSM steps along its sign
p = sig(w[0]*x[0] + w[1]*x[1] + b)
xa = [x[i] + eps*math.copysign(1, (p - y)*w[i]) for i in range(2)]
ok += (sig(w[0]*xa[0] + w[1]*xa[1] + b) >= 0.5) == bool(y)
return ok / len(data)
print(f"weights w=({w[0]:.3f}, {w[1]:.3f}) b={b:.3f}")
for eps in (0.0, 0.25, 0.5, 0.75, 1.0):
print(f"eps={eps:.2f} accuracy={acc(test, eps):.3f}")
Saída esperada:
weights w=(1.795, 1.756) b=-0.080
eps=0.00 accuracy=0.985
eps=0.25 accuracy=0.950
eps=0.50 accuracy=0.840
eps=0.75 accuracy=0.660
eps=1.00 accuracy=0.505
"""Lab P: label-flip poisoning and a backdoor trigger on a toy nearest-centroid
classifier (synthetic data, stdlib only). Teaching model only."""
import random
rng = random.Random(11)
def point(label, trigger=False):
cx = 1.0 if label else -1.0
x = [rng.gauss(cx, 0.8), rng.gauss(cx, 0.8), 0.0]
if trigger: x[2] = 5.0 # third feature is the 'trigger'
return x, label
clean = [point(i % 2) for i in range(400)]
test = [point(i % 2) for i in range(400)]
def fit(data):
c = {}
for lab in (0, 1):
pts = [x for x, y in data if y == lab]
c[lab] = [sum(p[i] for p in pts)/len(pts) for i in range(3)]
return c
def pred(c, x):
d = {k: sum((x[i]-v[i])**2 for i in range(3)) for k, v in c.items()}
return min(d, key=d.get)
def accuracy(c, data): return sum(pred(c, x) == y for x, y in data)/len(data)
print("label-flip poisoning (flip a fraction of class-1 labels to 0):")
for frac in (0.0, 0.1, 0.2, 0.3, 0.4):
data = [(x, y) for x, y in clean]; ones = [i for i, (x, y) in enumerate(data) if y == 1]
for i in ones[:int(frac*len(ones))]: data[i] = (data[i][0], 0)
print(f" flipped={frac:.1f} clean_test_accuracy={accuracy(fit(data), test):.3f}")
print("backdoor: add n triggered class-1 points labelled 0")
trig_test = [point(1, trigger=True) for _ in range(200)]
for n in (0, 10, 40, 80):
data = clean + [(point(1, trigger=True)[0], 0) for _ in range(n)]
c = fit(data)
asr = sum(pred(c, x) == 0 for x, _ in trig_test)/len(trig_test)
print(f" poison_points={n:3d} clean_accuracy={accuracy(c, test):.3f} attack_success={asr:.3f}")
Saída esperada:
label-flip poisoning (flip a fraction of class-1 labels to 0):
flipped=0.0 clean_test_accuracy=0.965
flipped=0.1 clean_test_accuracy=0.965
flipped=0.2 clean_test_accuracy=0.953
flipped=0.3 clean_test_accuracy=0.950
flipped=0.4 clean_test_accuracy=0.945
backdoor: add n triggered class-1 points labelled 0
poison_points= 0 clean_accuracy=0.965 attack_success=0.075
poison_points= 10 clean_accuracy=0.963 attack_success=0.145
poison_points= 40 clean_accuracy=0.963 attack_success=0.685
poison_points= 80 clean_accuracy=0.968 attack_success=0.980
"""Lab M: membership inference against an overfit 1-nearest-neighbour 'model'
that returns a confidence score. Synthetic records only; teaching model."""
import random, math
rng = random.Random(5)
def rec():
y = rng.random() < 0.5
return [rng.gauss(0.6 if y else -0.6, 1.0) for _ in range(4)], int(y)
members = [rec() for _ in range(150)]; nonmembers = [rec() for _ in range(150)]
def confidence(x, y, k):
# k-NN vote share for the true label, computed over the training set
d = sorted((sum((a-b)**2 for a, b in zip(x, m)), my) for m, my in members)
return sum(1 for _, my in d[:k] if my == y)/k
def auc(pos, neg):
s = sum((p > n) + 0.5*(p == n) for p in pos for n in neg)
return s/(len(pos)*len(neg))
for k in (1, 5, 25):
pos = [confidence(x, y, k) for x, y in members]
neg = [confidence(x, y, k) for x, y in nonmembers]
best = max(sum(p >= t for p in pos)/len(pos) - sum(n >= t for n in neg)/len(neg)
for t in sorted(set(pos + neg)))
print(f"k={k:2d} attack_auc={auc(pos, neg):.3f} best_advantage={best:.3f}")
Saída esperada:
k= 1 attack_auc=0.600 best_advantage=0.200
k= 5 attack_auc=0.521 best_advantage=0.073
k=25 attack_auc=0.520 best_advantage=0.060
"""Lab X: model extraction of a toy linear scoring API by querying it.
The 'API' returns a real-valued score; d+1 well-chosen queries recover the
weights exactly, and rounding the output limits fidelity. Synthetic model."""
import random
rng = random.Random(9)
d = 5
secret_w = [round(rng.uniform(-2, 2), 3) for _ in range(d)]; secret_b = 0.42
def api(x, decimals=None):
s = sum(a*b for a, b in zip(secret_w, x)) + secret_b
return round(s, decimals) if decimals is not None else s
for dec in (None, 2, 0):
b = api([0]*d, dec)
w = [api([1 if j == i else 0 for j in range(d)], dec) - b for i in range(d)]
err = max(abs(a-c) for a, c in zip(w, secret_w))
test = [[rng.uniform(-1, 1) for _ in range(d)] for _ in range(500)]
agree = sum((sum(a*c for a, c in zip(w, x))+b > 0) == (api(x) > 0) for x in test)/500
print(f"output_rounding={str(dec):4s} queries={d+1} max_weight_error={err:.3f} label_agreement={agree:.3f}")
Saída esperada:
output_rounding=None queries=6 max_weight_error=0.000 label_agreement=1.000
output_rounding=2 queries=6 max_weight_error=0.004 label_agreement=0.998
output_rounding=0 queries=6 max_weight_error=0.534 label_agreement=0.882
Rina executou os quatro em seu laptop com as sementes indicadas e comparou cada linha com a saída esperada antes de escrever qualquer coisa. Todas corresponderam. Quando a execução de evasão de um colega diferiu na terceira casa decimal, a causa era uma versão diferente do Python, que eles registraram nas notas de configuração. Conferir primeiro com a saída esperada pega diferenças assim antes que cheguem a um relatório ou a um avaliador.
25.3 Interpretação
Evasão: a acurácia cai à medida que o orçamento cresce. Envenenamento: a inversão de rótulos quase não importa para este modelo, mas um backdoor atinge 98% de sucesso de ataque com a acurácia limpa inalterada. Pertencimento: um modelo que memoriza vaza; um mais suave vaza pouco. Extração: uma API linear é copiada exatamente com seis consultas; arredondar as saídas reduz a fidelidade, mas não a elimina. Esses resultados trazem lições diferentes. Evasão e extração dependem do que o atacante pode consultar; o envenenamento depende de quem controla os dados de treinamento; o pertencimento depende de quanto o modelo memoriza. Cada um aponta para um controle diferente na LCH: limites de consulta e saídas grosseiras para o NoShow, proveniência dos rótulos para o DocSort.
25.4 Relatório de laboratório
Estrutura: objetivo, configuração (versões, sementes), método, resultados (tabelas), interpretação, limitações (modelos de brinquedo, dados sintéticos) e o que você testaria em seguida num sistema real, com autorização. O relatório de Rina declarou, nas limitações, que o laboratório de envenenamento usa um modelo linear minúsculo, então o número de 80 pontos não prevê quantas cartas seriam necessárias para implantar um backdoor no DocSort. O que se transfere é o padrão: o sucesso do ataque pode ser alto enquanto a acurácia limpa fica inalterada, então testes limpos, sozinhos, não conseguem detectar um backdoor.
25.4b Alteração de parâmetros
Um laboratório se torna um experimento quando você muda um parâmetro e prevê o efeito antes de executar. Tente mudar a lista de orçamentos de evasão, o número de pontos de backdoor ou os valores de k no laboratório de pertencimento; escreva sua previsão, execute e compare. Registre os parâmetros e a saída de cada execução. Esse hábito — prever, executar, comparar, registrar — é o núcleo do trabalho experimental nos capítulos seguintes e no projeto final. Rina previu que 20 pontos de backdoor dariam um sucesso de ataque entre os resultados de 10 e de 40 pontos, depois executou e registrou o valor ao lado de sua previsão.
25.4c Segurança do código de laboratório
Código de laboratório que implementa ataques deve ficar no laboratório. Não o adapte para atingir serviços reais nem compartilhe versões modificadas que apontem para endpoints reais. Os modelos de brinquedo aqui são deliberadamente simples para que os conceitos fiquem visíveis; seus ataques não são ferramentas eficazes contra sistemas reais, e isso é intencional. O repositório de Rina marca a pasta do laboratório como material didático e não guarda nela nenhuma configuração de endpoint. Os endereços dos payloads usam o domínio reservado, então nem mesmo um script copiado consegue enviar dados a algum lugar real.
25.5 Exemplo resolvido (caso sintético da LCH)
O relatório de laboratório de Rina ligou cada resultado de brinquedo a um sistema da LCH: a evasão às cartas elaboradas contra o DocSort, o envenenamento aos rótulos dos terceirizados, o pertencimento às saídas de confiança do NoShow e a extração a uma API para parceiros que estava planejada. Seus "próximos passos" propunham testes autorizados nas cópias de homologação da LCH. O líder de segurança da LCH aprovou os testes de homologação com um escopo escrito que cobria quais sistemas, quais datas e quais payloads podiam ser usados. O resultado de extração foi o mais persuasivo, porque a API planejada para parceiros devolvia escores exatos; a equipe concordou em arredondar as saídas antes do lançamento, aceitando a perda de fidelidade mostrada no laboratório.
25.6 Erros e limites
Três erros são comuns: apresentar resultados de brinquedo como fatos sobre sistemas reais; faltar versões e sementes; e ferramentas do laboratório apontadas para endpoints reais. Um modelo de brinquedo com cinco atributos informa sobre mecanismos, não sobre a robustez real do DocSort, então os resultados devem ser rotulados como resultados didáticos. A falta de versões e sementes torna um resultado impossível de reproduzir ou verificar. Apontar código de laboratório para serviços reais, mesmo por acidente, pode prejudicar pacientes e viola as regras sob as quais o laboratório é permitido; a configuração do laboratório de Rina contém apenas endereços locais.
Exercícios
- EX073 — Extração. Quantas consultas recuperam exatamente o modelo linear, e por quê? Rubrica: 3 = 6 (d + 1): uma para o viés, uma por peso; 2 = o número; 1 = vago; 0 = errado.
- EX074 — Interpretação. Por que a inversão de rótulos quase não altera o modelo de brinquedo? Rubrica: 3 = os centroides calculam a média de muitos pontos; inversões minoritárias deslocam pouco; 2 = parcial; 1 = vago; 0 = errado.
- EX075 — Relatório. Liste as sete partes do relatório de laboratório. Rubrica: 3 = as sete; 2 = cinco; 1 = três; 0 = nenhuma.
Síntese
O Laboratório I dá intuição prática sobre as quatro famílias de ataque e o hábito de relatórios de laboratório reprodutíveis e honestos.
Continue estudando — capítulo 25 de 40
← Capítulo 24 (rascunho) · Índice de capítulos do curso · Capítulo 26 (rascunho) →