AI Verification
main website: https://nguyenthanhvuh.github.io/class-verification/
No required book
Discussions in class
Bias
Safety
Robustness
Testing
Verifying
- If cannot find, the property is definitely satisfied
- Verification is less-used, only in specific domain (why?)
- Pros: provide proofs/guarantees. Higher confidence.
- Cons: expensive, requires source code
Testing shows the existence of bug, not its absence (Dijkstra).
We mainly focus on verification in this class.
y=max(x,0)
DNN Representation
DNN = And(n0_0 == If(i0 + -1*i1 <= 0, 0, i0 + -1*i1), n0_1 == If(i0 + i1 <= 0, 0, i0 + i1), n1_0 == If(1/2*n0_0 + -1/5*n0_1 <= 0, 0, 1/2*n0_0 + -1/5*n0_1), n1_1 == If(-1/2*n0_0 + 1/10*n0_1 <= 0, 0, -1/2*n0_0 + 1/10*n0_1), o0 == n1_0 + -1*n1_1, o1 == -1*n1_0 + n1_1)
Property Representation
- Precondition over inputs => postcondition over outputs
- If Pre then Post
If 1.0 >= i0 >= 0.0 and i1 <= 0.03 then o0 > o1
Verification as a SAT problem
- DNN = logical formula representing network
- Pre = logical formulae representing precondition
- Post = logical formula representing postcondition
We can use a constraint solver such as Z3 to query various things about this DNN from the obtained symbolic states:
z3.solve(symbolic_states)
[n0_1 = 15/2,
o1 = 1/2,
o0 = -1/2,
i1 = 7/2,
n1_1 = 1/2,
n1_0 = 0,
i0 = 4,
n0_0 = 1/2]
i0, i1, n0_0, n0_1, o0, o1 = z3.Reals("i0 i1 n0_0 n0_1 o0 o1")
# finding outputs when inputs are fixed [i0 == 1, i1 == -1]
g = z3.And([i0 == 1.0, i1 == -1.0])
z3.solve(z3.And(symbolic_states, g)) # we get o0, o1 = 1, -1
[n0_1 = 0,
o1 = -1,
o0 = 1,
i1 = -1,
n1_1 = 0,
n1_0 = 1,
i0 = 1,
n0_0 = 2]
Checking assertions
print("Prove that if (n0_0 > 0.0 and n0_1 <= 0.0) then o0 > o1")
g = z3.Implies(z3.And([n0_0 > 0.0, n0_1 <= 0.0]), o0 > o1)
print(g) # Implies(And(i0 - i1 > 0, i0 + i1 <= 0), o0 > o1)
z3.prove(z3.Implies(symbolic_states, g)) # proved
print("Prove that when (i0 - i1 > 0 and i0 + i1 <= 0), then o0 > o1")
g = z3.Implies(z3.And([i0 - i1 > 0.0, i0 + i1 <= 0.0]), o0 > o1)
print(g) # Implies(And(i0 - i1 > 0, i0 + i1 <= 0), o0 > o1)
z3.prove(z3.Implies(symbolic_states, g))
# proved
print("Disprove that when i0 - i1 >0, then o0 > o1")
g = z3.Implies(i0 - i1 > 0.0, o0 > o1)
print(g) # Implies(And(i0 - i1 > 0, i0 + i1 <= 0), o0 > o1)
z3.prove(z3.Implies(symbolic_states, g))
# counterexample
# [n0_1 = 15/2,
# i1 = 7/2,
# o0 = -1/2,
# o1 = 1/2,
# n1_0 = 0,
# i0 = 4,
# n1_1 = 1/2,
# n0_0 = 1/2]
implication