我对 C# 比较陌生,对我得到的 OOM 错误完全感到困惑。我正在尝试创建一个稀疏矩阵,因此正在收集(行索引、列索引、值)的三元组。在执行 for 循环时,最终发生的是进程使用的实际物理内存(根据资源管理器的说法,我相信 Windows 将其称为“工作集”)相对固定在 3.5GB 左右。但是,提交(我认为是虚拟内存)不断增加,直到达到提交限制,此时我的程序因 OOM 错误而崩溃。
相关代码如下:
public SimMatrix(string sparseMethod, string simMethod, Phrases phrases, DistrFeature features, int topK) {
List<int> rows = new List<int>(phrases.uniquePhraseCount*topK);
List<int> cols = new List<int>(phrases.uniquePhraseCount*topK);
List<double> vals = new List<double>(phrases.uniquePhraseCount*topK);
if (sparseMethod.Equals("invIdx")) {
List<int> nonzeros = new List<int>(features.inverted_idx.Count());
List<int> neighbors = new List<int>(phrases.uniquePhraseCount);
List<double> simVals = new List<double>(phrases.uniquePhraseCount);
List<int> sortedIdx = new List<int>(phrases.uniquePhraseCount);
List<double> sortedSim = new List<double>(phrases.uniquePhraseCount);
for (int i = 0; i < phrases.uniquePhraseCount; i++) { //loop through all phrases
using (SparseDoubleArray row = phrases.feature_values.GetRowSparse(i))
{
if (phrases.feature_values.RowLength(i) > 0) { //i.e., at least one feature fired for phrase
nonzeros = (from pmi in row.Elements select pmi.IndexList[1]).ToList();
neighbors = generateNeighbors(nonzeros, features.inverted_idx);
foreach (int neighbor in neighbors)
simVals.Add(cosineSimilarity(row, phrases.feature_values.GetRowSparse(neighbor)));
var sortedIdxSim = neighbors.Zip(simVals, (a, b) => new { idx = a, sim = b }).OrderByDescending(pair => pair.sim);
sortedIdx = sortedIdxSim.Select(pair => pair.idx).ToList();
sortedSim = sortedIdxSim.Select(pair => pair.sim).ToList();
int topN = (sortedIdxSim.Count() < topK) ? sortedIdxSim.Count() : topK;
rows.AddRange(Enumerable.Repeat(i, topN).ToList());
cols.AddRange(sortedIdx.Take(topN).ToList());
vals.AddRange(sortedSim.Take(topN).ToList());
nonzeros.Clear();
neighbors.Clear();
simVals.Clear();
sortedIdx.Clear();
sortedSim.Clear();
}
else { //just add self similarity
rows.Add(i);
cols.Add(i);
vals.Add(1);
}
Console.WriteLine("{0} phrases done", i + 1);
}
}
}
else { Console.WriteLine("Sorry, no other sparsification method implemented thus far"); }
simMat = new SparseDoubleArray(phrases.uniquePhraseCount, phrases.uniquePhraseCount, rows, cols, vals);
}
static private List<int> generateNeighbors(List<int> idx, Dictionary<int, List<int>> inverted_idx) {
List<int> neighbors = new List<int>();
foreach (int feature in idx) {
neighbors.AddRange(inverted_idx[feature]);
neighbors = neighbors.Distinct().ToList();
}
return neighbors;
}
static public double cosineSimilarity(SparseDoubleArray profile1, SparseDoubleArray profile2) {
double numerator = profile1.Dot(profile2);
double norm1 = profile1.Norm();
double norm2 = profile2.Norm();
double cos_sim = numerator / (norm1 * norm2);
if (cos_sim > 0)
return cos_sim;
else
return 0;
}
请注意,代码使用了一些内部库(例如,SparseDoubleArray 对象)。基本要点是我遍历所有条目(由 i 索引),并为每个条目找出非零列索引,然后通过“generateNeighbors”函数从中生成潜在邻居列表。一旦有了候选邻居列表,我就会计算每个潜在邻居的余弦相似度。然后,我同时对索引和相似度值进行排序,选择 topN 索引/相似度值,并将它们与索引 i(对应于行索引)一起添加到维护稀疏矩阵索引和值的列表中。
代码在执行 for 循环时看似不确定地中断。有时它在 i = 25,000 时中断,有时在 i = 2000 时中断。我什至没有进入初始化稀疏矩阵的阶段。
任何见解或帮助将不胜感激。
更新(2013 年 6 月 10 日)
感谢提供的响应,我设法大大减少了我的代码的提交内存。下面是更新后的代码,您会注意到它与问题的回复中的不完全相同,我将详细说明我需要更改的内容。
public SimMatrix(string sparseMethod, string simMethod, Phrases phrases, DistrFeature features, int topK) {
List<int> rows = new List<int>(phrases.uniquePhraseCount*topK);
List<int> cols = new List<int>(phrases.uniquePhraseCount*topK);
List<double> vals = new List<double>(phrases.uniquePhraseCount*topK);
if (sparseMethod.Equals("invIdx")) {
for (int i = 0; i < phrases.uniquePhraseCount; i++) { //loop through all phrases
using (SparseDoubleArray row = phrases.feature_values.GetRowSparse(i))
{
if (phrases.feature_values.RowLength(i) > 0) { //i.e., at least one feature fired for phrase
IEnumerable<int> nonzeros = from pmi in row.Elements select pmi.IndexList[1];
IEnumerable<int> neighbors = nonzeros.SelectMany(x => features.inverted_idx[x]).Distinct();
IEnumerable<double> simVals = neighbors.Select(x => cosineSimilarity(row, x, phrases));
var sortedIdxSim = neighbors.Zip(simVals, (a, b) => new { idx = a, sim = b }).OrderByDescending(pair => pair.sim).ToList();
//IEnumerable<int> sortedIdx = sortedIdxSim.Select(pair => pair.idx);
//IEnumerable<double> sortedSim = sortedIdxSim.Select(pair => pair.sim);
int sortedIdxSimCount = sortedIdxSim.Count;
int topN = (sortedIdxSimCount < topK) ? sortedIdxSimCount : topK;
rows.AddRange(Enumerable.Repeat(i, topN));
cols.AddRange(sortedIdxSim.Take(topN).Select(pair => pair.idx));
vals.AddRange(sortedIdxSim.Take(topN).Select(pair => pair.sim));
}
else { //just add self similarity
rows.Add(i);
cols.Add(i);
vals.Add(1);
}
if ((i % 1000) == 0)
Console.WriteLine("{0} phrases done;", i + 1);
}
}
}
else { Console.WriteLine("Sorry, no other sparsification method implemented thus far"); }
simMat = new SparseDoubleArray(phrases.uniquePhraseCount, phrases.uniquePhraseCount, rows, cols, vals);
}
static public double cosineSimilarity(SparseDoubleArray profile1, int profile2idx, Phrases phrases) {
using (SparseDoubleArray profile2 = phrases.feature_values.GetRowSparse(profile2idx)) {
double numerator = profile1.Dot(profile2);
double norm1 = profile1.Norm();
double norm2 = profile2.Norm();
double cos_sim = numerator / (norm1 * norm2);
if (cos_sim > 0)
return cos_sim;
else
return 0;
}
}
首先,我被迫将var sortedIdxSim
IEnumerable 转换为 List;这是因为我 a) 需要知道此列表中的元素数量,并且似乎调用.Count()
IEnumerable 会清除 IEnumerable 中保存的数据?似乎调用(例如,.Take()
根据IEnumerable<int> sortedIdx
Gjeltema 的原始建议)会清除IEnumerable<double> sortedSim
. 这是因为延期执行吗?我对延迟评估/延迟执行不太熟悉,所以也许我误解了我需要在这里做什么。
然而,老实说,这里的当前更改大大减少了我的提交内存,因此程序实际上可以运行到完成,非常感谢!如果有人可以帮助我澄清上述问题,那就太好了。