Latent Semantic Analysis is a statistical machine learning method based on singular value decomposition (SVD), and related to principal component analysis (PCA). Closely related features (words) in the model are combined into "concepts". Documents then get a con
(self, model, k=NORM)
| 1268 | class LSA(object): |
| 1269 | |
| 1270 | def __init__(self, model, k=NORM): |
| 1271 | """ Latent Semantic Analysis is a statistical machine learning method based on |
| 1272 | singular value decomposition (SVD), and related to principal component analysis (PCA). |
| 1273 | Closely related features (words) in the model are combined into "concepts". |
| 1274 | Documents then get a concept vector that is an approximation of the original vector, |
| 1275 | but with reduced dimensionality so that cosine similarity and clustering run faster. |
| 1276 | """ |
| 1277 | import numpy |
| 1278 | # Calling Model.vector() in a loop is quite slow, we should refactor this: |
| 1279 | matrix = [model.vector(d).values() for d in model.documents] |
| 1280 | matrix = numpy.array(matrix) |
| 1281 | # Singular value decomposition, where u * sigma * vt = svd(matrix). |
| 1282 | # Sigma is the diagonal matrix of singular values, |
| 1283 | # u has document rows and concept columns, vt has concept rows and term columns. |
| 1284 | u, sigma, vt = numpy.linalg.svd(matrix, full_matrices=False) |
| 1285 | # Delete the smallest coefficients in the diagonal matrix (i.e., at the end of the list). |
| 1286 | # The difficulty and weakness of LSA is knowing how many dimensions to reduce |
| 1287 | # (generally L2-norm is used). |
| 1288 | if k == L1: |
| 1289 | k = int(round(numpy.linalg.norm(sigma, 1))) |
| 1290 | if k == L2 or k == NORM: |
| 1291 | k = int(round(numpy.linalg.norm(sigma, 2))) |
| 1292 | if k == TOP300: |
| 1293 | k = max(0, len(sigma) - 300) |
| 1294 | if isinstance(k, int): |
| 1295 | k = max(0, len(sigma) - k) |
| 1296 | if type(k).__name__ == "function": |
| 1297 | k = max(0, int(k(sigma))) |
| 1298 | #print numpy.dot(u, numpy.dot(numpy.diag(sigma), vt)) |
| 1299 | # Apply dimension reduction. |
| 1300 | # The maximum length of a concept vector = the number of documents. |
| 1301 | assert k < len(model.documents), \ |
| 1302 | "can't create more dimensions than there are documents" |
| 1303 | tail = lambda list, i: range(len(list)-i, len(list)) |
| 1304 | u, sigma, vt = ( |
| 1305 | numpy.delete(u, tail(u[0], k), axis=1), |
| 1306 | numpy.delete(sigma, tail(sigma, k), axis=0), |
| 1307 | numpy.delete(vt, tail(vt, k), axis=0) |
| 1308 | ) |
| 1309 | # Store as Python dict and lists so we can pickle it. |
| 1310 | self.model = model |
| 1311 | self._terms = dict(enumerate(model.vector().keys())) # Vt-index => word. |
| 1312 | self.u, self.sigma, self.vt = ( |
| 1313 | dict((d.id, Vector((i, float(x)) for i, x in enumerate(v))) for d, v in izip(model, u)), |
| 1314 | list(sigma), |
| 1315 | [[float(x) for x in v] for v in vt] |
| 1316 | ) |
| 1317 | |
| 1318 | @property |
| 1319 | def terms(self): |
no test coverage detected