static_embeddings 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,8 +4,7 @@ require "static_embeddings"
4
4
 
5
5
  options = {
6
6
  min_cosine: 1.0 - 1e-6,
7
- max_abs: 1e-5,
8
- ids: true
7
+ max_abs: 1e-5
9
8
  }
10
9
 
11
10
  parser = OptionParser.new do |opts|
@@ -13,7 +12,6 @@ parser = OptionParser.new do |opts|
13
12
  opts.on("--oracle PATH") { |value| options[:oracle] = value }
14
13
  opts.on("--min-cosine N", Float) { |value| options[:min_cosine] = value }
15
14
  opts.on("--max-abs N", Float) { |value| options[:max_abs] = value }
16
- opts.on("--[no-]ids") { |value| options[:ids] = value }
17
15
  end
18
16
  parser.parse!(ARGV)
19
17
  unless options[:model] && options[:oracle]
@@ -22,12 +20,18 @@ end
22
20
 
23
21
  model = StaticEmbeddings.load(options[:model], verify: true)
24
22
  payload = JSON.parse(File.read(options[:oracle], encoding: "UTF-8"))
25
- rows = payload.is_a?(Hash) ? payload.fetch("rows") : payload
26
- oracle_max_length = payload.is_a?(Hash) ? payload["max_length"] : nil
23
+ abort "unsupported oracle schema #{payload["schema_version"].inspect}" unless payload["schema_version"] == 2
27
24
 
28
- if oracle_max_length && oracle_max_length != model.max_tokens
29
- warn "WARNING oracle max_length=#{oracle_max_length} but model.max_tokens=#{model.max_tokens}"
25
+ reference = payload.fetch("reference")
26
+ max_length = Integer(reference.fetch("max_length"))
27
+ if max_length != model.max_tokens
28
+ abort "oracle max_length=#{max_length} but model.max_tokens=#{model.max_tokens}"
30
29
  end
30
+ if reference["unk_token_id"] && Integer(reference["unk_token_id"]) != model.unk_id
31
+ abort "oracle unk_token_id=#{reference["unk_token_id"]} but model.unk_id=#{model.unk_id}"
32
+ end
33
+
34
+ rows = payload.fetch("rows")
31
35
 
32
36
  def dot(a, b)
33
37
  a.zip(b).sum { |x, y| x * y }
@@ -37,71 +41,98 @@ def norm(a)
37
41
  Math.sqrt(a.sum { |x| x * x })
38
42
  end
39
43
 
44
+ def vector_metrics(reference, got)
45
+ max_abs = reference.zip(got).map { |a, b| (a - b).abs }.max || 0.0
46
+ ref_zero = reference.all?(&:zero?)
47
+ got_zero = got.all?(&:zero?)
48
+ cosine =
49
+ if ref_zero && got_zero
50
+ 1.0
51
+ elsif ref_zero || got_zero
52
+ 0.0
53
+ else
54
+ dot(reference, got) / (norm(reference) * norm(got))
55
+ end
56
+ [cosine, max_abs]
57
+ end
58
+
59
+ raw_failures = []
60
+ usable_failures = []
61
+ vector_failures = []
62
+ invariant_failures = []
63
+ intentional_deviations = []
40
64
  min_cosine = 1.0
41
65
  max_abs_all = 0.0
42
- vector_failures = []
43
- id_failures = []
44
- id_rows_checked = 0
66
+ vectors_checked = 0
45
67
 
46
68
  rows.each_with_index do |row, i|
47
69
  text = row.fetch("text")
48
- ref = row.fetch("vector")
49
- ref_ids = row["token_ids"]
50
-
70
+ label = row.fetch("label", i.to_s)
51
71
  problems = []
52
72
 
53
- if options[:ids] && ref_ids
54
- id_rows_checked += 1
55
- got_ids = model.tokenize(text)
56
- if got_ids != ref_ids
57
- id_failures << i
58
- first = got_ids.zip(ref_ids).index { |a, b| a != b } || [got_ids.length, ref_ids.length].min
59
- problems << "ids differ at #{first} (got #{got_ids.length}, ref #{ref_ids.length})"
60
- end
73
+ expected_raw = row.fetch("hf_raw_token_ids")
74
+ got_raw = model.tokenize(text)
75
+ if got_raw != expected_raw
76
+ raw_failures << i
77
+ first = got_raw.zip(expected_raw).index { |a, b| a != b } || [got_raw.length, expected_raw.length].min
78
+ problems << "raw ids differ at #{first} (got #{got_raw.length}, ref #{expected_raw.length})"
61
79
  end
62
80
 
63
- got = model.embed(text).unpack("e*")
64
- max_abs = ref.zip(got).map { |a, b| (a - b).abs }.max || 0.0
65
- ref_zero = ref.all?(&:zero?)
66
- got_zero = got.all?(&:zero?)
67
- cosine =
68
- if ref_zero && got_zero
69
- 1.0
70
- elsif ref_zero || got_zero
71
- 0.0
72
- else
73
- dot(ref, got) / (norm(ref) * norm(got))
74
- end
81
+ full_raw = model.tokenize(text, max_tokens: false)
82
+ expected_usable = row.fetch("static_usable_token_ids")
83
+ got_usable = full_raw.reject { |id| id == model.unk_id }.first(max_length)
84
+ if got_usable != expected_usable
85
+ usable_failures << i
86
+ first = got_usable.zip(expected_usable).index { |a, b| a != b } || [got_usable.length, expected_usable.length].min
87
+ problems << "usable ids differ at #{first} (got #{got_usable.length}, ref #{expected_usable.length})"
88
+ end
75
89
 
76
- min_cosine = [min_cosine, cosine].min
77
- max_abs_all = [max_abs_all, max_abs].max
90
+ got_vector_blob = model.embed(text)
91
+ pooled_blob = model.embed_token_ids(full_raw)
92
+ unless got_vector_blob == pooled_blob
93
+ invariant_failures << i
94
+ problems << "embed(text) != embed_token_ids(unbounded tokenize(text))"
95
+ end
78
96
 
79
- unless cosine >= options[:min_cosine] && max_abs <= options[:max_abs]
80
- vector_failures << i
81
- problems << "vector out of tolerance"
97
+ model2vec_ids = row.fetch("model2vec_token_ids")
98
+ declared_deviation = row.fetch("model2vec_character_pretruncate_changes_ids")
99
+ actual_deviation = model2vec_ids != expected_usable
100
+ if actual_deviation != declared_deviation
101
+ problems << "oracle character-pretruncate flag is inconsistent"
102
+ usable_failures << i unless usable_failures.include?(i)
103
+ elsif actual_deviation
104
+ intentional_deviations << i
105
+ else
106
+ reference_vector = row.fetch("model2vec_vector")
107
+ got_vector = got_vector_blob.unpack("e*")
108
+ cosine, max_abs = vector_metrics(reference_vector, got_vector)
109
+ vectors_checked += 1
110
+ min_cosine = [min_cosine, cosine].min
111
+ max_abs_all = [max_abs_all, max_abs].max
112
+ unless cosine >= options[:min_cosine] && max_abs <= options[:max_abs]
113
+ vector_failures << i
114
+ problems << format("vector out of tolerance cos=%.10f max_abs=%.8g", cosine, max_abs)
115
+ end
82
116
  end
83
117
 
84
118
  status = problems.empty? ? "ok" : "FAIL"
85
- label = text.bytesize > 64 ? "#{text[0, 32].inspect}...(#{text.bytesize}B)" : text.inspect
86
- line = format("%s idx=%02d cos=%.10f max_abs=%.8g bytes=%d text=%s",
87
- status, i, cosine, max_abs, text.bytesize, label)
88
- line += " [#{problems.join('; ')}]" unless problems.empty?
89
- puts line
119
+ suffix = actual_deviation ? " intentional-character-pretruncate-deviation" : ""
120
+ puts "#{status} idx=#{format('%03d', i)} label=#{label.inspect} bytes=#{text.bytesize}#{suffix}" +
121
+ (problems.empty? ? "" : " [#{problems.join('; ')}]")
90
122
  end
91
123
 
92
124
  puts "rows=#{rows.length}"
93
- puts "id_rows_checked=#{id_rows_checked}"
94
- puts "min_cosine=#{min_cosine}"
95
- puts "max_abs_all=#{max_abs_all}"
96
- puts "token_id_failures=#{id_failures.inspect}"
125
+ puts "vectors_checked=#{vectors_checked}"
126
+ puts "intentional_character_pretruncate_deviations=#{intentional_deviations.length}"
127
+ puts "min_cosine=#{min_cosine}" if vectors_checked.positive?
128
+ puts "max_abs_all=#{max_abs_all}" if vectors_checked.positive?
129
+ puts "raw_token_id_failures=#{raw_failures.inspect}"
130
+ puts "usable_token_id_failures=#{usable_failures.inspect}"
131
+ puts "embed_invariant_failures=#{invariant_failures.inspect}"
97
132
  puts "vector_failures=#{vector_failures.inspect}"
98
133
 
99
- if id_rows_checked.zero? && options[:ids]
100
- warn "WARNING oracle has no token_ids; regenerate it with the current tools/model2vec_oracle.py"
101
- end
102
-
103
- unless id_failures.empty? && vector_failures.empty?
104
- abort "parity failed: token ids #{id_failures.inspect}, vectors #{vector_failures.inspect}"
134
+ unless raw_failures.empty? && usable_failures.empty? && invariant_failures.empty? && vector_failures.empty?
135
+ abort "parity failed"
105
136
  end
106
137
 
107
- puts "parity OK"
138
+ puts "corpus parity OK (#{rows.length}/#{rows.length}); intentional Model2Vec character pre-truncation deviations are reported separately"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: static_embeddings
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roman Haydarov