Spark MLlib學(xué)習(xí)——特征工程

Extracting, transforming and selecting features

這一大章節(jié)講的內(nèi)容主要是與特征工程相關(guān)的算法,粗略的可以分為如下幾類:

  1. Extraction:從Raw數(shù)據(jù)中提取出特征
  2. Transformation:Scaling, converting, or modifying features
  3. Selection:從大的特征集合中挑選一個(gè)子集
  4. Locality Sensitive Hashing (LSH):這類算法將特征變換的方面與其他算法相結(jié)合啊送。

Feature Extractors

TF-IDF

TF-IDF是Term frequency(詞頻)-inverse document frequency(逆文本頻率指數(shù))的縮寫辛馆。是一種在文本挖掘中廣泛使用的特征向量化方法唯卖,以反映一個(gè)單詞在語料庫中的重要性骄噪。定義:t 表示由一個(gè)單詞诸尽,d 表示一個(gè)文檔,D 表示語料庫(corpus)挨决,詞頻 TF(t,d) 表示某一個(gè)給定的單詞 t 出現(xiàn)在文檔 d 中的次數(shù)(單詞次數(shù))请祖, 而文檔頻率 DF(t,D) 表示包含單詞 t文檔次數(shù)。如果我們只使用詞頻 TF 來衡量重要性脖祈,則很容易過分強(qiáng)調(diào)出現(xiàn)頻率過高并且文檔包含少許信息的單詞肆捕,例如,'a'盖高,'the'慎陵,和 'of'眼虱。如果一個(gè)單詞在整個(gè)語料庫中出現(xiàn)的非常頻繁,這意味著它并沒有攜帶特定文檔的某些特殊信息(換句話說席纽,該單詞對(duì)整個(gè)文檔的重要程度低)捏悬。逆向文檔頻率是一個(gè)數(shù)字量度,表示一個(gè)單詞提供了多少信息:


其中润梯,|D| 是在語料庫中文檔總數(shù)过牙。由于使用對(duì)數(shù),所以如果一個(gè)單詞出現(xiàn)在所有的文件纺铭,其IDF值變?yōu)?寇钉。注意,應(yīng)用平滑項(xiàng)以避免在語料庫之外的項(xiàng)除以零(為了防止分母為0彤蔽,分母需要加1)摧莽。因此,TF-IDF測量只是TF和IDF的產(chǎn)物:(對(duì)TF-IDF定義為TF和IDF的乘積)

關(guān)于詞頻TF和文檔頻率DF的定義有多種形式顿痪。在MLlib镊辕,我們分離TF和IDF,使其靈活蚁袭。下面是MLlib中的使用情況簡單說明:

TFHashingTFCountVectorizer都可以用于生成詞頻TF向量征懈。
其中HashingTF是一個(gè)需要特征詞集的轉(zhuǎn)換器(Transformer),它可以將這些集合轉(zhuǎn)換成固定長度的特征向量揩悄。CountVectorizer將文本文檔轉(zhuǎn)換為關(guān)鍵詞計(jì)數(shù)的向量卖哎。

IDF:IDF是一個(gè)適合數(shù)據(jù)集并生成IDFModel的評(píng)估器(Estimator),IDFModel獲取特征向量(通常由HashingTFCountVectorizer創(chuàng)建)并縮放每列删性。直觀地說亏娜,它下調(diào)了在語料庫中頻繁出現(xiàn)的列。

代碼示例(Java版)

//創(chuàng)建Row的數(shù)據(jù)list
List<Row> data = Arrays.asList(
  RowFactory.create(0.0, "Hi I heard about Spark"),
  RowFactory.create(0.0, "I wish Java could use case classes"),
  RowFactory.create(1.0, "Logistic regression models are neat")
);
//創(chuàng)建Schema給Row定義數(shù)據(jù)類型和fieldName
StructType schema = new StructType(new StructField[]{
  new StructField("label", DataTypes.DoubleType, false, Metadata.empty()),
  new StructField("sentence", DataTypes.StringType, false, Metadata.empty())
});
Dataset<Row> sentenceData = spark.createDataFrame(data, schema);

//使用Tokenizer來將句子分割成單詞
Tokenizer tokenizer = new Tokenizer().setInputCol("sentence").setOutputCol("words");
Dataset<Row> wordsData = tokenizer.transform(sentenceData);

//使用HashingTF將句子中的單詞哈希成特征向量(這個(gè)可以在前一章節(jié)的最后輸出打印截圖中看到具體的值)
int numFeatures = 20;
HashingTF hashingTF = new HashingTF()
  .setInputCol("words")
  .setOutputCol("rawFeatures")
  .setNumFeatures(numFeatures);

Dataset<Row> featurizedData = hashingTF.transform(wordsData);
// alternatively, CountVectorizer can also be used to get term frequency vectors

//使用IDF對(duì)上面產(chǎn)生的特征向量進(jìn)行rescale
IDF idf = new IDF().setInputCol("rawFeatures").setOutputCol("features");
IDFModel idfModel = idf.fit(featurizedData); //fit得到IDF的模型

Dataset<Row> rescaledData = idfModel.transform(featurizedData); //對(duì)特征向量進(jìn)行rescale
rescaledData.select("label", "features").show();

//最后得到的特征向量可以作為其他機(jī)器學(xué)習(xí)算法的輸入
rescaledData.select("label", "words", "rawFeatures", "features").show()

Word2Vec

Word2Vec是一個(gè)Estimator(評(píng)估器)蹬挺,它采用表示文檔的單詞序列维贺,并訓(xùn)練一個(gè)Word2VecModel。 該模型將每個(gè)單詞映射到一個(gè)唯一的固定大小向量巴帮。 Word2VecModel使用文檔中所有單詞的平均值將每個(gè)文檔轉(zhuǎn)換為向量; 該向量然后可用作預(yù)測溯泣,文檔相似性計(jì)算等功能。有關(guān)更多詳細(xì)信息榕茧,請(qǐng)參閱有關(guān)Word2Vec的MLlib用戶指南垃沦。
代碼示例(Java版)

public class JavaWord2VecExample {
  public static void main(String[] args) {
    SparkSession spark = SparkSession
      .builder()
      .master("local[4]")
      .appName("JavaWord2VecExample")
      .getOrCreate();

    // $example on$
    // Input data: Each row is a bag of words from a sentence or document.
    List<Row> data = Arrays.asList(
      RowFactory.create(Arrays.asList("Hi I heard about Spark".split(" "))),
      RowFactory.create(Arrays.asList("I wish Java could use case classes".split(" "))),
      RowFactory.create(Arrays.asList("Logistic regression models are neat".split(" ")))
    );
    StructType schema = new StructType(new StructField[]{
      new StructField("text", new ArrayType(DataTypes.StringType, true), false, Metadata.empty())
    });
    Dataset<Row> documentDF = spark.createDataFrame(data, schema);

    //創(chuàng)建Word2Vec的實(shí)例,然后設(shè)置參數(shù)
    // Learn a mapping from words to Vectors.
    Word2Vec word2Vec = new Word2Vec()
      .setInputCol("text")
      .setOutputCol("result")
      .setVectorSize(3)
      .setMinCount(0);

    Word2VecModel model = word2Vec.fit(documentDF); //fit出模型
    Dataset<Row> result = model.transform(documentDF); //對(duì)輸入進(jìn)行transform得到結(jié)果DataFrame

    for (Row row : result.collectAsList()) {
      List<String> text = row.getList(0);
      Vector vector = (Vector) row.get(1);
      System.out.println("\n\nText: " + text + " => \nVector: " + vector + "\n\n\n");
    }
    // $example off$

    spark.stop();
  }
}
Word2Vec示例運(yùn)行結(jié)果

Feature Transformers

下圖是對(duì)特征轉(zhuǎn)換的API doc中列出的算法用押。不過這里不打算把每個(gè)都展開描述了肢簿,會(huì)簡單舉例幾個(gè),然后后面實(shí)際用到哪個(gè)再去查哪個(gè)。

API Doc中特征轉(zhuǎn)換算法列表

Tokenizer

Tokenization(文本符號(hào)化)是將文本 (如一個(gè)句子)拆分成單詞的過程译仗。(在Spark ML中)Tokenizer(分詞器)提供此功能抬虽。下面的示例演示如何將句子拆分為詞的序列官觅。
RegexTokenizer 提供了(更高級(jí)的)基于正則表達(dá)式 (regex) 匹配的(對(duì)句子或文本的)單詞拆分纵菌。默認(rèn)情況下,參數(shù)"pattern"(默認(rèn)的正則表達(dá)式: "\\s+"
休涤,此時(shí)和Tokenizer沒有區(qū)別
) 作為分隔符用于拆分輸入的文本咱圆。或者功氨,用戶可以將參數(shù)“gaps”設(shè)置為 false(不然默認(rèn)true的情況下分割出來的結(jié)果是分隔符的集合序苏,而不是單詞的集合),指定正則表達(dá)式"pattern"表示"tokens"捷凄,而不是分隔符忱详,這樣作為劃分結(jié)果找到的所有匹配項(xiàng)

回顧下正則表達(dá)式

代碼示例(Java版)

public class JavaTokenizerExample {
  public static void main(String[] args) {
    SparkSession spark = SparkSession
      .builder()
      .appName("JavaTokenizerExample")
      .getOrCreate();

    //構(gòu)造輸入數(shù)據(jù),注意第三行的數(shù)據(jù)word之間只有逗號(hào)沒有空格
    // $example on$
    List<Row> data = Arrays.asList(
      RowFactory.create(0, "Hi I heard about Spark"),
      RowFactory.create(1, "I wish Java could use case classes"),
      RowFactory.create(2, "Logistic,regression,models,are,neat")
    );

    StructType schema = new StructType(new StructField[]{
      new StructField("id", DataTypes.IntegerType, false, Metadata.empty()),
      new StructField("sentence", DataTypes.StringType, false, Metadata.empty())
    });

    Dataset<Row> sentenceDataFrame = spark.createDataFrame(data, schema);

    //Tokenizer劃分單詞是按照空格來做的
    Tokenizer tokenizer = new Tokenizer().setInputCol("sentence").setOutputCol("words");

    //通過setPattern來讓正則表達(dá)的劃分按照非字母來做
    RegexTokenizer regexTokenizer = new RegexTokenizer()
        .setInputCol("sentence")
        .setOutputCol("words")
        .setPattern("\\W");  // alternatively .setPattern("\\w+").setGaps(false); 換成這句話一樣的結(jié)果跺涤。

    //注冊(cè)一個(gè)user-defined functions匈睁,第一個(gè)參數(shù)是udf的名字,第二個(gè)參數(shù)是一個(gè)自定義的轉(zhuǎn)換函數(shù)桶错。
    spark.udf().register("countTokens", new UDF1<WrappedArray, Integer>() {
      @Override
      public Integer call(WrappedArray words) {
        return words.size();
      }
    }, DataTypes.IntegerType);

    //按照空格劃分,結(jié)果第三行沒有劃分,當(dāng)作整體對(duì)待葛碧。
    Dataset<Row> tokenized = tokenizer.transform(sentenceDataFrame);
    tokenized.select("sentence", "words")
        .withColumn("tokens", callUDF("countTokens", col("words"))).show(false);

    //按照正則表達(dá)式對(duì)待尊浓,把非字母的地方劃分了。
    Dataset<Row> regexTokenized = regexTokenizer.transform(sentenceDataFrame);
    regexTokenized.select("sentence", "words")
        .withColumn("tokens", callUDF("countTokens", col("words"))).show(false);
    // $example off$

    spark.stop();
  }
}
兩種Tokenizer

VectorAssembler(特征向量合并)

【這個(gè)API超級(jí)有用退腥!】VectorAssembler 是將指定的一list的列合并到單個(gè)列向量中的 transformer任岸。它可以將原始特征和不同特征transformers(轉(zhuǎn)換器)生成的特征合并為單個(gè)特征向量,來訓(xùn)練 ML 模型,如邏輯回歸和決策樹等機(jī)器學(xué)習(xí)算法狡刘。VectorAssembler 可接受以下的輸入列類型:任何數(shù)值型享潜、布爾類型向量類型颓帝。輸入列的值將按指定順序依次添加到一個(gè)向量中米碰。

舉例
假設(shè)現(xiàn)在有一個(gè)DataFrame,它的列為:id, hour, mobile, userFeatures和clicked:

id hour mobile userFeatures clicked
0 18 1.0 [0.0, 10.0, 0.5] 1.0

其中userFeatures是一個(gè)列向量包含三個(gè)用戶特征」撼牵現(xiàn)在想把hour, mobile, 和userFeatures合并到一個(gè)一個(gè)特征向量中(名為features吕座,這個(gè)也是很多MLlib算法默認(rèn)的特征輸入向量名字),然后用來預(yù)測clicked的值瘪板。具體用法就是將列hour吴趴、mobile和userFeatures作為input,features作為output侮攀,然后調(diào)用transform之后得到新的DataFrame:

id hour mobile userFeatures clicked features
0 18 1.0 [0.0, 10.0, 0.5] 1.0 [18.0, 1.0, 0.0, 10.0, 0.5]

示例Java代碼:

public class JavaVectorAssemblerExample {
  public static void main(String[] args) {
    SparkSession spark = SparkSession
      .builder()
      .appName("JavaVectorAssemblerExample")
      .getOrCreate();

    // $example on$
    StructType schema = createStructType(new StructField[]{
      createStructField("id", IntegerType, false),
      createStructField("hour", IntegerType, false),
      createStructField("mobile", DoubleType, false),
      createStructField("userFeatures", new VectorUDT(), false),
      createStructField("clicked", DoubleType, false)
    });
    Row row = RowFactory.create(0, 18, 1.0, Vectors.dense(0.0, 10.0, 0.5), 1.0);
    Dataset<Row> dataset = spark.createDataFrame(Arrays.asList(row), schema);
    System.out.println("\n-------Before assembled the original is:");
    dataset.show(false);

    VectorAssembler assembler = new VectorAssembler()
      .setInputCols(new String[]{"hour", "mobile", "userFeatures"})
      .setOutputCol("features");

    Dataset<Row> output = assembler.transform(dataset);
    System.out.println("\n+++++++Assembled columns 'hour', 'mobile', 'userFeatures' to vector column " +
        "'features'");
    output.select("features", "clicked").show(false);
    // $example off$

    spark.stop();
  }
}

運(yùn)行結(jié)果:

-------Before assembled the original is:
+---+----+------+--------------+-------+
|id |hour|mobile|userFeatures  |clicked|
+---+----+------+--------------+-------+
|0  |18  |1.0   |[0.0,10.0,0.5]|1.0    |
+---+----+------+--------------+-------+


+++++++Assembled columns 'hour', 'mobile', 'userFeatures' to vector column 'features'
+-----------------------+-------+
|features               |clicked|
+-----------------------+-------+
|[18.0,1.0,0.0,10.0,0.5]|1.0    |
+-----------------------+-------+


Feature Selectors

下面只介紹幾種MLlib提供的特特征選擇算法锣枝,其余參見API Doc厢拭,后續(xù)如果自己用到會(huì)再補(bǔ)充。

VectorSlicer(向量切片機(jī))

VectorSlicer是一個(gè)轉(zhuǎn)換器撇叁,它對(duì)于輸入的特征向量供鸠,輸出一個(gè)新的原始特征子集的特征向量。對(duì)于從列向量中提取特征很有幫助陨闹。
VectorSlicer對(duì)于指定索引的列向量楞捂,輸出一個(gè)新的列向量,所選擇的列向量通過這些索引進(jìn)行選擇趋厉。有兩種類型的索引:

  • 整數(shù)索引:代表列向量的下標(biāo)寨闹,setIndices()
  • 字符串索引:代表列的特征名稱,setNames()君账。這要求列向量有AttributeGroup繁堡,因?yàn)閷?shí)現(xiàn)中是在Attribute上的name字段匹配。

整數(shù)和字符串的索引都可以接受乡数。此外椭蹄,還可以同時(shí)使用整數(shù)索引和字符串名稱索引。但必須至少選擇一個(gè)特征瞳脓。重復(fù)的特征選擇是不允許的塑娇,所以選擇的索引和名稱之間不能有重疊。請(qǐng)注意劫侧,如果選擇了特征的名稱索引埋酬,則遇到空的輸入屬性時(shí)會(huì)拋出異常。
輸出時(shí)將按照選擇中給出的特征索引的先后順序進(jìn)行向量及其名稱的輸出烧栋。
舉例:
假設(shè)有一個(gè)DataFrame它的列名(AttributeGroup)為userFeatures

userFeatures
[0.0, 10.0, 0.5]

userFeatures是一個(gè)包含三個(gè)用戶特征的列向量写妥。假設(shè)userFeature的第一列全部為0,因此我們要?jiǎng)h除它并僅選擇最后兩列审姓。VectorSlicer使用setIndices(1,2)選擇最后兩個(gè)元素珍特,然后生成一個(gè)名為features的新向量列:

userFeatures features
[0.0, 10.0, 0.5] 10.0, 0.5

如果userFeatures已經(jīng)輸入了屬性值["f1", "f2", "f3"]魔吐,那么我們可以使用setNames("f2", "f3")來選擇它們扎筒。

userFeatures features
[0.0, 10.0, 0.5] 10.0, 0.5
["f1", "f2", "f3"] ["f2", "f2"]

下面是一個(gè)Java代碼示例:

public class JavaVectorSlicerExample {
  public static void main(String[] args) {
    SparkSession spark = SparkSession
      .builder()
      .appName("JavaVectorSlicerExample")
      .getOrCreate();

    //構(gòu)造AttributeGroup來方便vectorSlicer使用setNames
    // $example on$
    Attribute[] attrs = new Attribute[]{
      NumericAttribute.defaultAttr().withName("f1"),
      NumericAttribute.defaultAttr().withName("f2"),
      NumericAttribute.defaultAttr().withName("f3")
    };
    AttributeGroup group = new AttributeGroup("userFeatures", attrs);

    //構(gòu)造數(shù)據(jù)
    List<Row> data = Lists.newArrayList(
      RowFactory.create(Vectors.sparse(3, new int[]{0, 1}, new double[]{-2.0, 2.3}).toDense()), //這里必須使用toDense()來避免sprse的數(shù)據(jù)結(jié)構(gòu)引起下面的切片時(shí)的問題酬姆。
      RowFactory.create(Vectors.dense(-2.0, 2.3, 0.0)) //dense和sparse的區(qū)別在與sparse是稀疏的適合大量0數(shù)據(jù)的構(gòu)造嗜桌,dense是把每個(gè)數(shù)值都要賦值的適合非稀疏的情況。
    );

    Dataset<Row> dataset =
      spark.createDataFrame(data, (new StructType()).add(group.toStructField()));
    System.out.println("\n=======Original DataFrame is:");
    dataset.show(false);

    //構(gòu)造VectorSlicer設(shè)置輸入列為"userFeatures"辞色,輸出列為“features”
    VectorSlicer vectorSlicer = new VectorSlicer()
      .setInputCol("userFeatures").setOutputCol("features");

    //setIndices和setNames來選擇int[]和String[]的特征列
    vectorSlicer.setIndices(new int[]{1}).setNames(new String[]{"f3"});
    // or slicer.setIndices(new int[]{1, 2}), or slicer.setNames(new String[]{"f2", "f3"})

    Dataset<Row> output = vectorSlicer.transform(dataset);
    System.out.println("\n---------After slice select the output DataFrame is:");
    output.show(false);
    // $example off$

    spark.stop();
  }
}
VectorSlicer的輸出結(jié)果

如果對(duì)于sparse向量不使用toDense方法那么結(jié)果就是對(duì)sparse結(jié)構(gòu)的數(shù)據(jù)進(jìn)行slice操作骨宠,結(jié)果如下:


錯(cuò)誤的VectorSlicer結(jié)果,目前我已將這個(gè)小bug在github上提交了pull request

Locality Sensitive Hashing

LSH是哈希技術(shù)中重要的一種,通常用于集群层亿,近似最近鄰搜索和大型數(shù)據(jù)集的孤立點(diǎn)檢測桦卒。
LSH的大致思路是用一系列函數(shù)(LSH families)將數(shù)據(jù)哈希到桶中,這樣彼此接近的數(shù)據(jù)點(diǎn)處于相同的桶中可能性就會(huì)很高匿又,而彼此相距很遠(yuǎn)的數(shù)據(jù)點(diǎn)很可能處于不同的桶中方灾。一個(gè)LSH family 正式定義如下。
在度量空間(M,d)中琳省,M是一個(gè)集合迎吵,d是M上的一個(gè)距離函數(shù)躲撰,LSH family是一系列能滿足以下屬性的函數(shù)h


滿足以上條件的LSH family被稱為(r1, r2, p1, p2)-sensitive针贬。
在Spark中,不同的LSH families實(shí)現(xiàn)在不同的類中(例如:MinHash)拢蛋,并且在每個(gè)類中提供了用于特征變換的API桦他,近似相似性連接和近似最近鄰。
在LSH中谆棱,我們將一個(gè)假陽性定義為一對(duì)相距大的輸入特征(當(dāng) d(p,q)≥r2 時(shí))快压,它們被哈希到同一個(gè)桶中,并且將一個(gè)假陰性定義為一對(duì)相鄰的特征(當(dāng) d(p,q)≤r1 時(shí) )垃瞧,它們被分散到不同的桶中蔫劣。

LSH Operations(LSH運(yùn)算)

我們描述了大部分LSH會(huì)用到的運(yùn)算,每一個(gè)合適的LSH模型都有自己的方法實(shí)現(xiàn)了這些運(yùn)算个从。

Feature Transformation(特征變換)

特征變換是將哈希值添加為新列的基本功能脉幢。 這可以有助于降低維數(shù)。 用戶可以通過設(shè)置 inputCol 和 outputCol 參數(shù)來指定輸入和輸出列名嗦锐。
LSH 還支持多個(gè)LSH哈希表嫌松。 用戶可以通過設(shè)置 numHashTables 來指定哈希表的數(shù)量。 這也用于近似相似性連接和近似最近鄰的 OR-amplification(或放大器)放大奕污。 增加哈希表的數(shù)量將增加準(zhǔn)確性萎羔,但也會(huì)增加通信成本和運(yùn)行時(shí)間。
outputCol 的類型是 Seq [Vector]碳默,其中數(shù)組的維數(shù)等于 numHashTables 贾陷,并且向量的維度當(dāng)前設(shè)置為1。在將來的版本中嘱根,我們將實(shí)現(xiàn) AND-amplification(與放大器)髓废,以便用戶可以指定這些向量的維度 。

Approximate Similarity Join(近似相似度連接)

近似相似度連接采用兩個(gè)數(shù)據(jù)集儿子,并且近似返回距離小于用戶定義閾值的數(shù)據(jù)集中的行對(duì)瓦哎。 近似相似度連接支持兩個(gè)不同的數(shù)據(jù)集連接和自連接。 Self-joinin (自連接)會(huì)產(chǎn)生一些重復(fù)的對(duì)。
近似相似度連接接受已轉(zhuǎn)換和未轉(zhuǎn)換的數(shù)據(jù)集作為輸入蒋譬。 如果使用未轉(zhuǎn)換的數(shù)據(jù)集割岛,它將自動(dòng)轉(zhuǎn)換。 在這種情況下犯助,哈希簽名將被創(chuàng)建為outputCol癣漆。
在加入的數(shù)據(jù)集中,可以在數(shù)據(jù)集A和數(shù)據(jù)集B中查詢?cè)紨?shù)據(jù)集剂买。 距離列將被添加到輸出數(shù)據(jù)集惠爽,以顯示返回的每對(duì)行之間的真實(shí)距離。

Approximate Nearest Neighbor Search(近似最鄰近搜索)

近似最近鄰搜索采用數(shù)據(jù)集(特征向量)和Key鍵(單個(gè)特征向量)瞬哼,并且它近似返回?cái)?shù)據(jù)集中最接近向量的指定數(shù)量的行婚肆。
近似最近鄰搜索接受已轉(zhuǎn)換和未轉(zhuǎn)換的數(shù)據(jù)集作為輸入。 如果使用未轉(zhuǎn)換的數(shù)據(jù)集坐慰,它將自動(dòng)轉(zhuǎn)換较性。 在這種情況下,哈希簽名將被創(chuàng)建為outputCol结胀。
距離列將被添加到輸出數(shù)據(jù)集赞咙,以顯示每個(gè)輸出行和搜索的鍵之間的真實(shí)距離。
注意:當(dāng)哈希桶中沒有足夠的候選項(xiàng)時(shí)糟港,近似最近鄰搜索將返回少于k行攀操。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市秸抚,隨后出現(xiàn)的幾起案子速和,更是在濱河造成了極大的恐慌,老刑警劉巖耸别,帶你破解...
    沈念sama閱讀 212,185評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件健芭,死亡現(xiàn)場離奇詭異,居然都是意外死亡秀姐,警方通過查閱死者的電腦和手機(jī)慈迈,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,445評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來省有,“玉大人痒留,你說我怎么就攤上這事〈姥兀” “怎么了伸头?”我有些...
    開封第一講書人閱讀 157,684評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長舷蟀。 經(jīng)常有香客問我恤磷,道長面哼,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,564評(píng)論 1 284
  • 正文 為了忘掉前任扫步,我火速辦了婚禮魔策,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘河胎。我一直安慰自己闯袒,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,681評(píng)論 6 386
  • 文/花漫 我一把揭開白布游岳。 她就那樣靜靜地躺著政敢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪胚迫。 梳的紋絲不亂的頭發(fā)上喷户,一...
    開封第一講書人閱讀 49,874評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音晌区,去河邊找鬼摩骨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛朗若,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播昌罩,決...
    沈念sama閱讀 39,025評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼哭懈,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了茎用?” 一聲冷哼從身側(cè)響起遣总,我...
    開封第一講書人閱讀 37,761評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎轨功,沒想到半個(gè)月后旭斥,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,217評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡古涧,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,545評(píng)論 2 327
  • 正文 我和宋清朗相戀三年垂券,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片羡滑。...
    茶點(diǎn)故事閱讀 38,694評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡菇爪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出柒昏,到底是詐尸還是另有隱情凳宙,我是刑警寧澤,帶...
    沈念sama閱讀 34,351評(píng)論 4 332
  • 正文 年R本政府宣布职祷,位于F島的核電站氏涩,受9級(jí)特大地震影響届囚,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜是尖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,988評(píng)論 3 315
  • 文/蒙蒙 一奖亚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧析砸,春花似錦昔字、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,778評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至弦疮,卻和暖如春夹攒,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背胁塞。 一陣腳步聲響...
    開封第一講書人閱讀 32,007評(píng)論 1 266
  • 我被黑心中介騙來泰國打工咏尝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人啸罢。 一個(gè)月前我還...
    沈念sama閱讀 46,427評(píng)論 2 360
  • 正文 我出身青樓编检,卻偏偏與公主長得像,于是被迫代替她去往敵國和親扰才。 傳聞我的和親對(duì)象是個(gè)殘疾皇子允懂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,580評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容