-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProgram.cs
More file actions
271 lines (237 loc) · 8.26 KB
/
Copy pathProgram.cs
File metadata and controls
271 lines (237 loc) · 8.26 KB
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
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
using PrivacyFilterNet;
BenchmarkSwitcher.FromAssembly(typeof(PrivacyFilterBenchmarks).Assembly).Run(args);
[MemoryDiagnoser]
public class PrivacyFilterBenchmarks
{
private const string ShortInput =
"Alice Smith can be reached at alice@example.com or +1 (425) 555-0100.";
private readonly string _longInput = string.Join(
Environment.NewLine,
Enumerable.Repeat(ShortInput, 50));
private PrivacyFilter? _filter;
[GlobalSetup]
public void Setup()
{
string modelDirectory =
Environment.GetEnvironmentVariable("PRIVACY_FILTER_MODEL_DIR")
?? throw new InvalidOperationException(
"Set PRIVACY_FILTER_MODEL_DIR to an openai/privacy-filter checkpoint.");
_filter = PrivacyFilter.Load(modelDirectory);
}
[GlobalCleanup]
public void Cleanup() => _filter?.Dispose();
[Benchmark(Baseline = true)]
public PrivacyFilterResult ShortText() => _filter!.Redact(ShortInput);
[Benchmark]
public PrivacyFilterResult LongText() => _filter!.Redact(_longInput);
}
[MemoryDiagnoser]
public class ManagedPostprocessingBenchmarks
{
private const int TokenCount = 4096;
private float[] _scores = null!;
private float[] _logProbabilities = null!;
private float[] _denseStartScores = null!;
private float[] _denseEndScores = null!;
private float[] _denseTransitionScores = null!;
private ViterbiDecoder _decoder = null!;
private int _classCount;
[GlobalSetup]
public void Setup()
{
LabelSpace labels = LabelSpace.Create(CreateClassNames());
_decoder = new ViterbiDecoder(labels, calibrationPath: null);
_classCount = labels.TokenClassNames.Length;
_scores = new float[TokenCount * _classCount];
_logProbabilities = new float[_scores.Length];
var random = new Random(42);
for (int index = 0; index < _scores.Length; index++)
{
_scores[index] = (random.NextSingle() * 20) - 10;
}
PrivacyFilter.LogSoftmax(
_scores,
_logProbabilities,
TokenCount,
_classCount,
destinationTokenOffset: 0);
CreateDenseScores(
_classCount,
out _denseStartScores,
out _denseEndScores,
out _denseTransitionScores);
}
[Benchmark]
public void LogSoftmax() =>
PrivacyFilter.LogSoftmax(
_scores,
_logProbabilities,
TokenCount,
_classCount,
destinationTokenOffset: 0);
[Benchmark(Baseline = true)]
public int[] ViterbiDense() =>
DecodeDense(
_logProbabilities,
TokenCount,
_classCount,
_denseStartScores,
_denseEndScores,
_denseTransitionScores);
[Benchmark]
public int[] ViterbiSparse() => _decoder.Decode(_logProbabilities, TokenCount);
[Benchmark]
public int[] ArgMaxScalar() =>
ArgMaxScalar(_logProbabilities, TokenCount, _classCount);
[Benchmark]
public int[] ArgMaxTensorPrimitives() =>
PrivacyFilter.ArgMax(_logProbabilities, TokenCount, _classCount);
private static int[] ArgMaxScalar(
ReadOnlySpan<float> scores,
int tokenCount,
int classCount)
{
var labels = new int[tokenCount];
for (int token = 0; token < tokenCount; token++)
{
int offset = token * classCount;
int bestLabel = 0;
float bestScore = scores[offset];
for (int label = 1; label < classCount; label++)
{
float score = scores[offset + label];
if (score > bestScore)
{
bestScore = score;
bestLabel = label;
}
}
labels[token] = bestLabel;
}
return labels;
}
private static int[] DecodeDense(
float[] emissions,
int tokenCount,
int classCount,
float[] startScores,
float[] endScores,
float[] transitionScores)
{
var previousScores = new float[classCount];
var nextScores = new float[classCount];
var backpointers = new int[(tokenCount - 1) * classCount];
for (int label = 0; label < classCount; label++)
{
previousScores[label] = emissions[label] + startScores[label];
}
for (int token = 1; token < tokenCount; token++)
{
int emissionOffset = token * classCount;
int backpointerOffset = (token - 1) * classCount;
for (int next = 0; next < classCount; next++)
{
float bestScore = float.NegativeInfinity;
int bestPrevious = 0;
for (int previous = 0; previous < classCount; previous++)
{
float score = previousScores[previous] +
transitionScores[(previous * classCount) + next];
if (score > bestScore)
{
bestScore = score;
bestPrevious = previous;
}
}
nextScores[next] = bestScore + emissions[emissionOffset + next];
backpointers[backpointerOffset + next] = bestPrevious;
}
(previousScores, nextScores) = (nextScores, previousScores);
}
int lastLabel = 0;
float bestFinalScore = float.NegativeInfinity;
for (int label = 0; label < classCount; label++)
{
float score = previousScores[label] + endScores[label];
if (score > bestFinalScore)
{
bestFinalScore = score;
lastLabel = label;
}
}
var path = new int[tokenCount];
path[^1] = lastLabel;
for (int token = tokenCount - 2; token >= 0; token--)
{
lastLabel = backpointers[(token * classCount) + lastLabel];
path[token] = lastLabel;
}
return path;
}
private static void CreateDenseScores(
int classCount,
out float[] startScores,
out float[] endScores,
out float[] transitionScores)
{
const float invalid = -1e9f;
startScores = Enumerable.Repeat(invalid, classCount).ToArray();
endScores = Enumerable.Repeat(invalid, classCount).ToArray();
transitionScores = Enumerable.Repeat(invalid, classCount * classCount).ToArray();
for (int previous = 0; previous < classCount; previous++)
{
char? previousTag = Boundary(previous);
if (previous == 0 || previousTag is 'B' or 'S')
{
startScores[previous] = 0;
}
if (previous == 0 || previousTag is 'E' or 'S')
{
endScores[previous] = 0;
}
for (int next = 0; next < classCount; next++)
{
if (IsValidTransition(previous, next))
{
transitionScores[(previous * classCount) + next] = 0;
}
}
}
}
private static bool IsValidTransition(int previous, int next)
{
char? previousTag = Boundary(previous);
char? nextTag = Boundary(next);
if (previous == 0 || previousTag is 'E' or 'S')
{
return next == 0 || nextTag is 'B' or 'S';
}
return SpanLabel(previous) == SpanLabel(next) && nextTag is 'I' or 'E';
}
private static char? Boundary(int label) => label == 0 ? null : "BIES"[(label - 1) % 4];
private static int SpanLabel(int label) => label == 0 ? 0 : ((label - 1) / 4) + 1;
private static string[] CreateClassNames()
{
var names = new List<string> { "O" };
foreach (string label in new[]
{
"account_number",
"private_address",
"private_date",
"private_email",
"private_person",
"private_phone",
"private_url",
"secret",
})
{
foreach (char boundary in "BIES")
{
names.Add($"{boundary}-{label}");
}
}
return names.ToArray();
}
}