1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
#!/usr/bin/env ruby
require 'zlib'
STDOUT.set_encoding 'utf-8'
STDOUT.sync = true
def ngrams_it(s, n, fix=false)
a = s.strip.split
a.each_with_index { |tok, i|
tok.strip!
0.upto([n-1, a.size-i-1].min) { |m|
yield a[i..i+m] if !(fix^(a[i..i+m].size==n))
}
}
end
class NamedSparseVector
attr_accessor :h
def initialize init=nil
@h = {}
@h = init if init
@h.default = 0.0
end
def + other
new_h = Hash.new
new_h.update @h
ret = NamedSparseVector.new new_h
other.each_pair { |k,v| ret[k]+=v }
return ret
end
def - other
new_h = Hash.new
new_h.update @h
ret = NamedSparseVector.new new_h
other.each_pair { |k,v| ret[k]-=v }
return ret
end
def * scalar
raise ArgumentError, "Arg is not numeric #{scalar}" unless scalar.is_a? Numeric
ret = NamedSparseVector.new
@h.keys.each { |k| ret[k] = @h[k]*scalar }
return ret
end
def dot other
sum = 0.0
@h.each_pair { |k,v|
sum += v * other[k]
}
return sum
end
def [] k
@h[k]
end
def []= k, v
@h[k] = v
end
def each_pair
@h.each_pair { |k,v| yield k,v }
end
def to_s
@h.to_s
end
def size
@h.keys.size
end
end
def sparse_vector_test
a = NamedSparseVector.new
b = NamedSparseVector.new
a["a"] = 1
b["b"] = 1
c = NamedSparseVector.new
c += (a-b)*0.1
puts "a=#{a.to_s}, b=#{b.to_s}, (a-b)*0.1 = #{c.to_s}"
end
def write_model fn, w
Zlib::GzipWriter.open(fn) do |gz|
gz.write w.to_s+"\n"
end
end
def read_model fn
Zlib::GzipReader.open(fn) do |gz|
return NamedSparseVector.new eval(gz.read)
end
end
def usage
STDERR.write "#{__FILE__} <config file>\n"
exit 1
end
usage if ARGV.size != 1
def read_cfg fn
begin
f = File.new fn, 'r'
rescue
STDERR.write "#{__FILE__}: Can't find file '#{fn}', exiting.\n"
exit 1
end
cfg = {}
while line = f.gets
next if /^\s*$/.match line
k, v = line.strip.split /\s*=\s*/, 2
cfg[k] = v unless k[0]=='#' # no inline comments
end
return cfg
end
def parse_example s
a = s.split
label = a[0].to_f
fv = NamedSparseVector.new
a[1..a.size-2].each { |i|
name,val = i.split ':'
fv[name] = val.to_f
}
return [label, fv]
end
# main
cfg = read_cfg ARGV[0]
silent = true if cfg['silent']
max_iter = 1000
max_iter = cfg['max_iter'].to_i if cfg['max_iter']
errors = 0
start = Time.now
w = NamedSparseVector.new
bias = 0
train = []
train_f = File.new cfg['train'], 'r'
while line = train_f.gets
train << parse_example(line.strip)
end
train_f.close
test = []
if cfg['test']
test_f = File.new cfg['test'], 'r'
while line = test_f.gets
test << parse_example(line.strip)
end
test_f.close
end
iter = 0
while true
err = 0
train.each_with_index { |i, idx|
if (i[0] * (w.dot(i[1]) + bias)) <= i[0]
w += i[1] * i[0]
bias += i[0]
err += 1
end
}
puts "iter:#{iter} err=#{err}"
iter += 1
break if err==0 || iter==max_iter
end
elapsed = Time.now-start
puts "#{elapsed.round 2} s, #{(elapsed/Float(iter+1)).round 2} s per iter; model size: #{w.size}" if !silent
puts cfg['model_file']
write_model cfg['model_file'], w
if cfg['test']
test_err = 0
test.each { |i|
if (i[0] * (w.dot(i[1]) + bias)) <= i[0]
test_err += 1
end
}
puts "test error=#{test_err}"
end
|