Pommes rot-weiss, mit Currywurst auch Proloteller genannt. Assoziert eine solche Mahlzeit doch eher wenig Niveau. Wer die Shootouts kennt weiss, dass es nicht ganz so einfach ist. Ich schweife ab.
Rot-weiss scheint als eher für einfach - billig - zu stehen. Überprüfung der These anhand von Fussball. 1. Bundesliga: nix rot-weiss. 2. Bundesliga: nix rot-weiss. Regionalliga: Rot-Weiß Erfurt, Rot-Weiss Essen, Rot-Weiß Oberhausen, Rot-Weiss Ahlen. Der empirische Beweis ist erbracht: rot-weiss steht für billig.
Rubrik: Angewandter Schwachsinn
Nein, mitnichten was jetzt einige Leser denken mögen. Es geht nicht um freizügig agierende Mitarbeiter. Ich hab nur gerade festgestellt, dass mir inzwischen nicht nur das Betriebssystem egal ist - ich habe sie alle gehabt, C64, Amiga, Atari ST, DOS, Windows 1.0 - XP, AIX, Solaris, BSDs, Linux, Mac OS (7 - X). Mir scheint inzwischen auch die Office-Anwendung egal zu sein. NeoOffice, Lotus 123, Ami Pro, OpenOffice, StarWriter, Word 2.0 ( DOS), Numbers, Excel 3 unter OS/2, M$-Office oder Pages - egal. Eben bemerkt, Numbers ist offen, Word und NeoOffice. Habe lustig zwischen den dreien hin und her kopiert und dann ein PDF ausgedruck ![]()
Ja, endlich ist es soweit. Heute fängt die dritte Staffel an. Bin mal gespannt, wie das ohne den Bruce so läuft....Aber zum Glück wird im Ersten weitergeheult. Drama, Baby, Drama....
Allerdings erinnern mich die Werbeplakate in den Haltestellen eher an "Du bist Deutschland".....Heidi weltoffen, Barbara und Lena etwas verklemmt...
Bin auch gespannt über die Farbe der Koffer, die es in dieser Staffel gibt ![]()
Vor ein paar Tagen begann der Internetzugang von Alice zu zicken. Einwahl ging nicht mehr - authentication failed. Heute habe ich es wieder hinbekommen. Im D-Link G664T darf an den Benutzernamen nicht mehr die Kennung @hansenet.de gehängt werden. Ebenso muss der VPI-Wert auf 1 und der VCI-Wert auf 32 gesetzt werden - statt 8 für VPI und 35 für VCI. MTU und MRU sind jeweils 1492.
Bilder anklicken und geniessen.
Ein bizarres Problem. Ich schmeisse knapp 500.000 Element in eine HashMap. Die HashMap wurde mit einer initialen Kapazität von 500.000 erzeugt. Das Einfügen - inklusive parsen einer XML-Datei - dauert auf dem Mac ca. 9.200ms. Auf einem vergleichbaren Linux-System ca. 8.000ms. Soweit, so gut.
Bizarr wird es bei Abfragen. 100 look ups auf dem Mac werden in 99ms abgearbeitet - wiederholbar. Auf dem Linux-System sind es immer mehr als 21.000ms. Egal, ob ich Beas JRockit verwende, das JDK von Sun oder das JDK von IBM. Ob 5. oder 6. JDK ist ebenfalls unerheblich. Der Prozess hat genug Hauptspeicher (1,5 GB) und lagert nicht auf Platte aus - der JIT kann wunderbar optimieren. Wird eine TreeMap an Stelle der HashMap verwendet, tritt das gleiche Phänomen auf.
Im Augenblick sitze ich mit grossen Augen vor dem Problem und weiss nicht weiter.
Nachtrag: Die Beschreibung oben ist nicht korrekt. Ich rufe die HashMap nicht direkt auf, sondern über eine Groovy Methode. Dieser Methodenaufruf frisst die Zeit. Es ist nicht die Implementierung der HashMap. Deren look up Zeit ist im Millisekundenbereich nicht messbar. Soweit also mein Fehler. Jetzt gilt es heraus zu finden, wie ich den Groovy-Teil beschleunigen kann und ob dies überhaupt geht. Grund: der Aufruf erfolgt über einen sun.reflect.GeneratedMethodAccessor. Erschüttert bin ich darüber, dass Apples Implementierung soviel besser ist.
Nachtrag 2: Ich habe das Problem jetzt ziemlich dreckig gelöst. Das Feld der HashMap habe ich public gemacht und greife jetzt direkt darauf zu.
Die DKP Landtagsabgeordnete in Niedersachsen - Christel Wegner - hat gemeint, dass für den Aufbau einer neuen Staatsform soetwas wie die Stasi wie eingeführt werden müsse. Weil man sich auch davor schützen muss, dass andere Kräfte, reaktionäre Kräfte, die Gelegenheit nutzen und so einen Staat von innen aufweichen.
Damit hat sie sich in der eigenen Linken Rhetorik verfangen - heillos.
Hehlerei (Deutschland)
Bildung einer kriminellen Vereinigung (Liechtenstein)
Die Lucene Performanz in ε·ο·s entäuschte. Heute habe ich Daniel um Rat gefragt. Er wies mich auch den Lucene FieldCache hin und eine Diskussion über die Implementierung. Ich habe es in ε·ο·s implementiert. Bei Tests habe ich festgestellt, dass der Cache volllief, weil ich den IndexReader wieder verwendet habe. Der Reader wird jetzt jedesmal neu initialisiert. Der Performanzgewinn ist erstaunlich. Benötigte eine Abfrage vorher schon mal 30 Sekunden ist die Zeit jetzt fast immer deutlich unter 1 Sekunde.
Alter Code (Groovy)
def searcher = new IndexSearcher(reader)
def hits = searcher.search(query)
def retval = []
for (int i = 0; i < hits.length() && i < COUNT; i++) {
def Document doc = hits.doc(i)
def id = doc.get(FIELD_ID)
def year = doc.get(FIELD_YEAR)
def idYear = new IdYear();
idYear.id = id
idYear.year = year
retval.add(idYear)
}
searcher.close()
Neuer Code (Groovy)
def reader = IndexReader.open(directory)
def searcher = new IndexSearcher(reader)
def hits = searcher.search(query, (Filter) null, COUNT);
def idsCache = FieldCache.DEFAULT.getStrings(reader, FIELD_ID);
def yearsCache = FieldCache.DEFAULT.getStrings(reader, FIELD_YEAR);
def retval = []
if (hits.scoreDocs.length != 0) {
for (i in 0..(hits.scoreDocs.length - 1)) {
def id = idsCache[hits.scoreDocs[i].doc];
def year = yearsCache[hits.scoreDocs[i].doc];
def idYear = new IdYear();
idYear.id = id
idYear.year = year
retval.add(idYear)
}
}
searcher.close()
reader.close()
Die Morgenröte ist in einer ersten Beta-Version fertig. ε·ο·s ist ein einfache Front-End für eine Entity Oriented Search über Medline-Daten. Ich habe die Daten in den letzten Wochen erstellt (siehe hier, hier, hier, hier und hier). Dabei ist mir beim Erstellen des Volltext Index ein Fehler unterlaufen. Anstatt des Lucene WhitespaceAnalyzer habe ich den StandardAnalyzer verwendet. Deswegen muss der Index für die endgültige Version noch einmal neu berechnet werden. Für die Entwicklung der Oberfläche habe ich einen 5% grossen Index erstellt. Wegen dieser beiden Gründe entsprechen die folgenden Screenshots nicht den realen Ergenissen. Leider werde ich die spätere Version auch nicht auf diesem Serverchen hier bereit stellen können - der Ressourcenverbrauch ist einfach zu gross.
Die Oberfläche ist einfach. Der Benutzer kann in einer Eingabezeile Terme oder Phrasen eingeben. Er kann das Jahr auswählen, für welches die Eingabe gelten soll - default: alle Jahre.
![Phraseneingabe [ε·ο·s Phraseneingabe]](http://www.speexx.de/blog/images/2008/02/eos1.png)
Phraseneingabe
Das Ergebniss wird ein einer einfachen Tabelle dargestellt. Die Spalten zeigen die laufende Nummer, den preferred Term des Concepts und das entsprechende Jahr. Kommt ein Concept mehrfach vor (Jahresauswahl in allen Jahren), wird nur das erste Vorkommen genommen.
![Lookup Ergebniss [ε·ο·s Lookup Ergebniss]](http://www.speexx.de/blog/images/2008/02/eos2.png)
Lookup Ergebniss für 'dopamine' in 2006
Ein Problem ist die Geschwindigkeit des Lookups. Die Suche in Lucene dauert meistens nur wenige 100 Millisekunden. Die Ergebnisse dann zu holen ist allerdings sehr teuer und kann über eine halbe Minuten dauern. Ich denke, dass ich hier einen Fehler mache. Muss mal Daniel fragen, wie ich die Abfrage etwas beschleunigen kann.
Ich habe die Applikation in Grails entwickelt. Grails ist ein auf Groovy basierender Ruby on Rails Clone. Ich habe Grails gewählt, weil ich das Werkzeug einmal ausprobieren wollte. Nach 5 Stunden war bei ε·ο·s der Durchstich geschafft: die Anwendung zeigte Ergebnisse. Das spricht für die Dokumentation, aber, zugegeben, ich kannte die Groovy Syntax schon ein wenig
Danach habe ich noch ein wenig Feintuning durchgeführt. Die gesamte Umsetzung des Clients hat ca. 7 Stunden gedauert.
Mein Eindruck vom Duo Groovy/Grails ist zwiespältig. Zum Einen empfand ich es mal wieder spannend einfach drauflos zu scripten. Zum Anderen bin ich durch die Typsicherheit, die mir Java bietet sehr verwöhnt. Die meisten Fehler, die ich gemacht habe, waren vergessene oder falsch geschriebene Property- und Methodennamen. Hier ist die IDE-Unterstützung noch nicht so gut.
So wie es aussieht in ich der 6658. Beschwerdeführer gegen die Verfassungsklage gegen die Vorratsdatenspeicherung. 6658 von bisher über 85.000 auf der Liste.
Im zweiten Schritt der Backend-Kette (siehe Eintrag von gestern) für eine Entity Oriented Search werden sehr grosse Dokumente erzeugt. Zu gross für ein bearbeiten auf commodity Hardware. Die Dokumente für alle Jahre (year = XXXX) mit den Konzepten brain und liver sind jweils knapp 124 MB gross geworden. Hadoop ist mit einem OutOfMemoryError ausgestiegen, wenn org.apache.hadoop.io.Text#write aufgerufen wurde - bei patient würde das Ergebniss vermutlich noch desaströser werden. In den Java-Tiefen wurde ein ByteArrayInputStream kopiert. Ich hätte natürlich den Speicher erhöhen können. Statt 1 GB hätte ich es mit 1,2 GB oder mehr versuchen können. Bei 3 Prozessen würde das mit dem Hauptspeicher der Rechner (4 GB) gerade noch so hinhauen, dass sie nicht swappen. Was aber wenn die Dokumentenmenge grösser wird? Deswegen berechne ich die Daten jetzt neu, nur auf Jahres-Basis.
Vermutlich werde ich den Zwischenschritt sowieso eleminieren, da ich aus diesem direkt den Lucene Index bauen kann. Das ist natürlich Blödsinn - es widerspricht der Idee, dass jeweils ein Programm für eine Aufgabe da sein soll. Immerhin will ich den Lucene Index mit unterschiedlichen Normalisierungen berechnen.
Nebenbei habe ich auch noch den Code für die Strategy beim Fully Distributed Cache angepasst.
Das letzte Glied der Backend-Kette für eine Entity Oriented Search ist fertig.
In einem ersten Schritt habe ich Medline Abstracts in Sätze zerlegt und eventuell doppelt vorhandene heraus gefiltert. In einem zweiten Schritt habe ich relevante Konzepte identifiziert (Erkenntnisse darüber in HadoopTestCase und der DistributedCache). Hierfür habe ich einen firmeneigenen Thesaurus verwendet - die Identifikation wurde mittels eines Tries durchgeführt.
Für jedes Konzept habe ich zu jedem Jahr, in welches es vorkommt, alle Sätze zusammen gefasst. In diesem letzten Schritt wurden diese Konzept-Jahr-Satz Tupel in einen Lucene Index überführt. Die Basis dafür hat Christian geschrieben und ich habe seinen Basis übernommen und für meine Anforderungen angepasst. Um den Index nach dem optimieren zu prüfen wurde wieder ein HadoopTestCase erstellt. Der Index wird aus Testdaten erstellt. Um ihn zu testen, muss der Zielpfad allerdings um "merge-output" erweitert werden (hart codiert in IndexMerger).
Job Konfiguration:
final JobConf jobConf = createJobConf();
jobConf.setMapperClass(IndexingMapper.class);
jobConf.setReducerClass(IndexingReducer.class);
jobConf.setJobName("testindex");
jobConf.setOutputFormat(OutputFormatLucene.class); // von Christian
jobConf.setOutputValueClass(Text.class);
Mapper:
public void map(final K key,
final Text value,
final OutputCollector output,
final Reporter reporter) throws IOException {
// hier passiert gar nix.
output.collect(key, value);
}
Reducer:
public void reduce(final K key,
final Iterator lineIterator,
final OutputCollector output,
final Reporter reporter) throws IOException {
while (lineIterator.hasNext()) {
final Text textValue = lineIterator.next();
final Document doc = createDocumentFromText(textValue, reporter);
if (doc != null) {
output.collect(key, new ObjectWritable(doc));
}
}
}
Nach der Abarbeitung des Jobs:
import org.apache.nutch.indexer.FsDirectory;
import org.apache.nutch.indexer.IndexMerger;
final FileSystem fs = FileSystem.get(jobConf);
final IndexMerger merger = new IndexMerger(jobConf);
merger.merge(fs.listPaths(new Path[] {inputPath}),
outputPath,
new Path(outputPath, "tempdir"));
final FsDirectory dir =
new FsDirectory(fs,
new Path(this.resultPath, "merge-output"),
false,
jobConf);
final IndexSearcher searcher = new IndexSearcher(dir);
searcher.setSimilarity(new NormedLengthSimilarity());
final QueryParser qp = new QueryParser(CONTENT.name(),
OutputFormatLucene.ANALYZER);
final String escaped = QueryParser.escape("urn:123");
final Query q = qp.parse(CONTENT.name() + ":\"" + escaped + "\"");
final Hits hits = searcher.search(q);
assertEquals(2, hits.length());
searcher.close();
Als nächstes muss ich den kompletten Index zweimal erstellen. Einmal mit den Lucene Bord-Mitteln und einmal mit einer angepassten Similarity. Hierbei wird beim Indexieren die lengthNorm auf 1.0f gesetzt, damit kurze Dokumente nicht bevorzugt gewertet werden. Ein einfaches Web-Frontend soll dann suchen und vergleichen ermöglichen.
Ich habe heute in eine Hadoop MapReduce-Applikation mit einem DistributedCache geschrieben. Grund: ich wollte eine 100 MB grosse Datei nicht in die uberjar einbauen. Hadoops DistributedCache ist für solche nur-lese Dateien bestens geeignet. Für die Benutzung ist auf der Hadoop Seite im WordCount v2.0 Beispiel auch eine ausreichende Anleitung. Nach ein paar anfänglichen Schwierigkeiten (wer lesen kann ist klar im Vorteil, siehe vorhergehenden Satz) klappte es auch ganz ordentlich. Leider nicht mit dem HadoopTestCase.
Ich habe den Pfade zur Datei im MapReduce-Driver der Konfiguration übergeben:
static final String TRIEX_DAT = "simple.triex";
public static final String LOCAL_PATH;
static {
final Thread currentThread = Thread.currentThread();
final ClassLoader loader = currentThread.getContextClassLoader();
final URL resource = loader.getResource(PARAGRAPHS_DAT);
final String path = resource.getPath();
final int lastIndexOf = path.lastIndexOf("/");
LOCAL_PATH = path.substring(0, lastIndexOf);
}
DistributedCache.addCacheFile(new Path(LOCAL_PATH, TRIEX_DAT).toUri(),
jobConf);
Im Mapper wurde der Pfad dann wie geholt und weiter verarbeitet:
final Path[] triexFile = DistributedCache.getLocalCacheFiles(this.jobConf);
final InputStream in = new FileInputStream(triexFile[0].toString());
Ergebnis:
Caused by: java.lang.NullPointerException at org.apache.hadoop.util.StringUtils.stringToPath(StringUtils.java:197) at org.apache.hadoop.filecache.DistributedCache.getLocalCacheFiles(DistributedCache.java:515) at example.DocumentProducerMapper.initializeConceptMapper(DocumentProducerMapper.java:365)
Scheinbar wird der Pfad nicht richtig übergeben, weil es sich nicht um ein fully-distributed System handelt. Die Lösung sieht wie folgt aus:
final URI[] triexFile =
DistributedCache.getCacheFiles(this.jobConf);
final Path p = new Path(triexFile[0].toASCIIString());
final FileSystem fs = p.getFileSystem(this.jobConf);
final Path qualified = p.makeQualified(fs);
final InputStream in = qualified.toUri().toURL().openStream();
Damit die Tests durchlaufen habe ich das Strategie-Pattern angewendet. Es gibt eine default-Strategie für den Cluster-Betrieb. Diese wird verwendet, wenn keine andere Strategie verwendet wird, um die DistributedCache Pfade aufzulösen. Die Strategie-Klasse wird in der Job-Konfiguration hinterlegt. Ist der Inhalt nicht null, wird daraus ein neues Exemplar gebaut und verwendet. Nicht ganz elegant - für die Experimente allerdings ausreichend.
Strategie-Interface und -Klasse:
package example;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.mapred.JobConf;
import java.io.IOException;
public interface DistributedCacheStrategy {
String KEY = "example.DistributedCacheStrategy.classname";
Path[] distributedCachePathes(final JobConf conf) throws IOException;
}
package example;
import org.apache.hadoop.filecache.DistributedCache;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.mapred.JobConf;
import java.io.IOException;
public class FullyDistributedCacheStrategy implements DistributedCacheStrategy {
public Path[] distributedCachePathes(final JobConf conf) throws IOException
{
final List pathes = new ArrayList();
final Path[] pathList = DistributedCache.getLocalCacheFiles(conf);
for (final Path path : pathList) {
final Path qualified = new Path("file", "", path.toString());
pathes.add(qualified);
}
return pathes.toArray(new Path[pathList.length]);
}
}
package example;
import org.apache.hadoop.filecache.DistributedCache;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.mapred.JobConf;
import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
public class TestDistributedCacheStrategy implements DistributedCacheStrategy {
public Path[] distributedCachePathes(final JobConf conf) throws IOException
{
final URI[] uris = DistributedCache.getCacheFiles(conf);
final List pathes = new ArrayList();
for (final URI uri : uris) {
final Path p = new Path(uri.toASCIIString());
final FileSystem fs = p.getFileSystem(conf);
final Path qualified = p.makeQualified(fs);
pathes.add(qualified);
}
return pathes.toArray(new Path[uris.length]);
}
}
Sicherheit überdeckt alles: Sicherheit vs Privatsphäre II
Frisch schickt mir zwei Mail. Betreff der zweite Mail: Nie mehr giessen
.
In der ersten, zwei Minuten zuvor, fragt er Alkoholiker? Kein Problem mehr
. Schön, dann ist das je geklärt.
Miller hingegen will mir einen Patio-Heater verkaufen. In der Heimat meiner Lieblings-Patios - Cordoba - war es heute 23℃ warm. Unser heimischer Patio ist keine 2m². Miller, ich bin mir sicher, wir kommen nicht ins Geschäft.
Ansonsten: alles fit, brauche auch kein Viagra.
Hier und dort wird in den letzten Tagen viel darüber geschrieben, dass Konkurrenz das Geschäft belebt. Es wird bloss übersehen, dass bei der geplanten Übernahme von Yahoo durch Micro$oft aus drei zwei werden sollen. Das ist ein Konkurrent weniger. Nicht mehr.
Nach der Medline Abstract Satzzerlegung habe ich heute im Hadoop-Cluster doppelte Sätze eleminiert. Hierfür werden die Abstracts mit dem java.text.BreakIterator in Sätze zerlegt. Die erzeugten Sätze werden normalisiert. Für die normalisierten Sätze wird ein MD5 berechnet. In der Mapping-Phase werden alle Sätze mit ihrem MD5 als key rausgeschrieben. Die Reduce-Phase nimmt dann den ersten Satz zu dem key und schreibt diesen mit dem key wieder raus. Alle weiteren Sätze werden ignoriert. In einem Vergleich wird nicht der MD5-Algorithmus verwendet, sondern der SHA-512
Ergebnis
| Sätze insgesamt | MD5 | SHA-512 |
|---|---|---|
88.333.921¹ | 85.746.464 | 85.746.464 |
Bewertung
Keine Hash-Kollisionen - MD5 ist ausreichend. 2.587.457 Sätze kommen 2- oder mehrfach vor.
Benchmark
Es wurden 17.021.733 Medline Abstracts bearbeitet. Diese lagen in einer Eingabedatei von 11,92 GB. Berechnet wurde auch einem Hadoop-Cluster aus 7 Maschinen der Entry-Server Klasse (4 GB RAM, Xeon Quad-Core, da Dual-Cores nicht lieferbar war
, 500 GB Festplatte).
| MD5 | SHA-512 | |
|---|---|---|
| Dauer der Berechnung | 11 Minuten | 15 Minuten, 46 Sekunden |
| Reduce Output | 14,45 GB | 22,43 GB |
[¹] Leicht modifizierter satzzerlege Algorithmus gegenüber den Ergebnissen vom 2008-02-02.
Habe heute auf 17.021.733 Medline Abstracts 3 Java Satzzerleger angewendet. Stand des Datenumfangs ist von Ende November 2007. Die Titel der Abstracts wurden als Sätze an die Abstract-Texte angehängt. Für die Satzzerlegung wurde verwendet:
- der
BreakIteratoraus Java 5 - der
BreakIteratoraus dem ICU-Projekt (Version 3.8.1) - dem LingPipe
SentenceChunkervon Alias-I mitMedlineSentenceModel(Version 3.2.0)
Ergebnis
Sätze
| Sentencer | Sätze |
|---|---|
java.text.BreakIterator | 88.334.044 |
com.ibm.icu.text.BreakIterator | 89.406.705 |
com.aliasi.sentences.SentenceChunker | 87.880.299 |
Unterschiede
Java BreakIterator | ICU BreakIterator | LingPipe SentenceChunker | |
|---|---|---|---|
Java 5 BreakIterator | 1,21% | -0,51% | |
ICU BreakIterator | -1,20% | -1,71% | |
LingPipe SentenceChunker | 0,52% | 1,74% |
Fazit
Ich gehe davon aus, dass weniger Sätze eine exaktere Zerlegung bedeutet. Aus dieser Sicht ist das Werkzeug LingPipe am effektivsten. Der ICU BreakIterator das schlechteste Werkzeug. Bei den Kosten von US$ 9.500 bis US$ 44.000 für LingPipe ist die um ca. 0,5% schlechtere Performanz des Java BreakIterators tolerierbar. Dies gilt auch in Hinblick auf das Ziel: Entity Oriented Search

![Email des Rixdorfer Gladiatoren [BAHNHOFSSCHILD FÜR BERLIN "RIXDORF" - KLEIN DARUNTER: GLADIATOR - EMAIL]](http://www.speexx.de/blog/images/2008/02/email%20des%20rixdorfer%20gladiators.th.jpg)
![Weltrevolution by H&M [WERBEPLAKAT VON H&M AN EINER KOMPLETTEN HÄUSERWAND. DARUNTER EIN GRAFITTI MIT DEM TITEL "WELTREVOLUTION"]](http://www.speexx.de/blog/images/2008/02/weltrevolution.th.jpg)
![Gartenkulturpfad [SCHILD MIT DER AUFSCHRIFT "GARTENKULTURPFAD" - IM HINTERGRUND SIND 5 BAUKRÄNE AUF DER MUSEUMSINSEL IN BERLIN MITT ZU SEHEN]](http://www.speexx.de/blog/images/2008/02/gartenkulturpfad.th.jpg)