TensorFlow入门(Getting Started With TensorFlow)

(全文Google翻译,自学使用)
本指南让您开始在TensorFlow中编程。 在使用本指南之前,请安装TensorFlow。 为了充分利用本指南,您应该了解以下内容:

This guide gets you started programming in TensorFlow. Before using this guide,install TensorFlow. To get the most out of this guide, you should know the following:

  • 如何用Python编程。
  • 至少了解一点数组。
  • 理想的是,了解关于机器学习的东西。 但是,如果您对机器学习知之甚少,那么这仍然是您应该阅读的第一本指南。
  • How to program in Python.
  • At least a little bit about arrays.
  • Ideally, something about machine learning. However, if you know little or nothing about machine learning, then this is still the first guide you should read.

TensorFlow提供了多个API。 最低级别的API - TensorFlow核心 - 为您提供完整的编程控制。 我们推荐TensorFlow Core用于机器学习研究人员和其他需要对其模型进行精细控制的人员。 更高层次的API建立在TensorFlow核心之上。 这些更高级别的API通常比TensorFlow Core更容易学习和使用。 另外,更高级别的API使得不同用户之间的重复任务更容易和更一致。 像tf.estimator这样的高级API可以帮助您管理数据集,估算器,培训和推理。

TensorFlow provides multiple APIs. The lowest level API--TensorFlow Core-- provides you with complete programming control. We recommend TensorFlow Core for machine learning researchers and others who require fine levels of control over their models. The higher level APIs are built on top of TensorFlow Core. These higher level APIs are typically easier to learn and use than TensorFlow Core. In addition, the higher level APIs make repetitive tasks easier and more consistent between different users. A high-level API like tf.estimator helps you manage data sets, estimators, training and inference.

本指南从TensorFlow核心教程开始。 稍后,我们演示如何在tf.estimator中实现相同的模型。 了解TensorFlow核心原则将给你一个很好的思维模型,说明在使用更紧凑的更高级别的API时,内部工作是如何工作的。

This guide begins with a tutorial on TensorFlow Core. Later, we demonstrate how to implement the same model in tf.estimator. Knowing TensorFlow Core principles will give you a great mental model of how things are working internally when you use the more compact higher level API.

张量(Tensors)

TensorFlow中的数据中心单位是张量。 一个张量由一组形成任意数量维数的原始值组成。 张量的rank是它的维数。 这里是一些张量的例子:

The central unit of data in TensorFlow is the tensor. A tensor consists of a set of primitive values shaped into an array of any number of dimensions. A tensor's rank is its number of dimensions. Here are some examples of tensors:

3 # a rank 0 tensor; a scalar with shape []
[1., 2., 3.] # a rank 1 tensor; a vector with shape [3]
[[1., 2., 3.], [4., 5., 6.]] # a rank 2 tensor; a matrix with shape [2, 3]
[[[1., 2., 3.]], [[7., 8., 9.]]] # a rank 3 tensor with shape [2, 1, 3]

TensorFlow核心教程(TensorFlow Core tutorial)

导入Tensorflow(Importing TensorFlow)

TensorFlow程序的规范导入语句如下所示:

The canonical import statement for TensorFlow programs is as follows:

import tensorflow as tf

这使得Python可以访问所有TensorFlow的类,方法和符号。 大多数文档假定您已经完成了这个工作。

This gives Python access to all of TensorFlow's classes, methods, and symbols. Most of the documentation assumes you have already done this.

计算图(The Computational Graph)

你可能会想到TensorFlow核心程序由两个独立的部分组成:

  1. 构建计算图。
  2. 运行计算图。

You might think of TensorFlow Core programs as consisting of two discrete sections:

  1. Building the computational graph.
  2. Running the computational graph.

计算图是排列成由节点组成的图的一系列TensorFlow操作。 我们来构建一个简单的计算图。 每个节点将零个或多个张量作为输入,并生成张量作为输出。 一种节点的类型是一个常量。 像所有的TensorFlow常量一样,它不需要输入,而是输出一个内部存储的值。 我们可以创建两个浮点张量node1node2,如下所示:

A computational graph is a series of TensorFlow operations arranged into a graph of nodes. Let's build a simple computational graph. Each node takes zero or more tensors as inputs and produces a tensor as an output. One type of node is a constant. Like all TensorFlow constants, it takes no inputs, and it outputs a value it stores internally. We can create two floating point Tensors node1 and node2 as follows:

node1 = tf.constant(3.0, dtype=tf.float32)
node2 = tf.constant(4.0) # also tf.float32 implicitly
print(node1, node2)

最后的打印声明产生

The final print statement produces

Tensor("Const:0", shape=(), dtype=float32) Tensor("Const_1:0", shape=(), dtype=float32)

请注意,打印节点不会像预期的那样输出值3.0和4.0。 相反,它们是在评估时分别产生3.0和4.0的节点。 为了实际评估节点,我们必须在会话中运行计算图。 会话封装了TensorFlow运行时的控制和状态。

Notice that printing the nodes does not output the values 3.0 and 4.0 as you might expect. Instead, they are nodes that, when evaluated, would produce 3.0 and 4.0, respectively. To actually evaluate the nodes, we must run the computational graph within a session. A session encapsulates the control and state of the TensorFlow runtime.

以下代码创建一个Session对象,然后调用其run方法来运行足够的计算图来评估node1和node2。 通过在会话中运行计算图如下:

The following code creates a Session object and then invokes its run method to run enough of the computational graph to evaluate node1 and node2. By running the computational graph in a session as follows:

sess = tf.Session()
print(sess.run([node1, node2]))

我们看到了3.0和4.0的预期值:

we see the expected values of 3.0 and 4.0:

[3.0, 4.0]

我们可以通过将Tensor节点与操作相结合来构建更复杂的计算(操作也是节点)。 例如,我们可以添加我们的两个常量节点,并产生一个新的图形如下:

We can build more complicated computations by combining Tensor nodes with operations (Operations are also nodes). For example, we can add our two constant nodes and produce a new graph as follows:

from __future__ import print_function
node3 = tf.add(node1, node2)
print("node3:", node3)
print("sess.run(node3):", sess.run(node3))

最后两个打印语句产生

The last two print statements produce

node3: Tensor("Add:0", shape=(), dtype=float32)
sess.run(node3): 7.0

TensorFlow提供了一个名为TensorBoard的实用程序,可以显示计算图的图片。 下面是一个屏幕截图,展示了TensorBoard如何将图形可视化:

TensorFlow provides a utility called TensorBoard that can display a picture of the computational graph. Here is a screenshot showing how TensorBoard visualizes the graph:

就目前来看,这张图并不是特别有趣,因为它总是会产生一个不变的结果。 图可以参数化来接受外部输入,称为占位符。 占位符是稍后提供值的承诺。

As it stands, this graph is not especially interesting because it always produces a constant result. A graph can be parameterized to accept external inputs, known as placeholders. A placeholder is a promise to provide a value later.

a = tf.placeholder(tf.float32)
b = tf.placeholder(tf.float32)
adder_node = a + b  # + provides a shortcut for tf.add(a, b)

前面的三行有点像一个函数或lambda,我们在其中定义两个输入参数(a和b),然后对它们进行操作。 我们可以通过使用run方法的feed_dict参数将多个输入的具体值提供给占位符来评估这个图形。

The preceding three lines are a bit like a function or a lambda in which we define two input parameters (a and b) and then an operation on them. We can evaluate this graph with multiple inputs by using the feed_dict argument to the run method to feed concrete values to the placeholders:

print(sess.run(adder_node, {a: 3, b: 4.5}))
print(sess.run(adder_node, {a: [1, 3], b: [2, 4]}))

结果输出

resulting in the output

7.5
[ 3.  7.]

在TensorBoard中,图看起来像这样:

In TensorBoard, the graph looks like this:

我们可以通过添加另一个操作来使计算图更加复杂。 例如,

We can make the computational graph more complex by adding another operation. For example,

add_and_triple = adder_node * 3.
print(sess.run(add_and_triple, {a: 3, b: 4.5}))

产生的输出

produces the output

22.5

在TensorBoard中,上面的计算图如下所示:

The preceding computational graph would look as follows in TensorBoard:

在机器学习中,我们通常需要一个可以进行任意输入的模型,比如上面的模型。 为了使模型可训练,我们需要能够修改图形以获得具有相同输入的新输出。 变量允许我们将可训练参数添加到图形中。 它们由一个类型和初始值所构成:

In machine learning we will typically want a model that can take arbitrary inputs, such as the one above. To make the model trainable, we need to be able to modify the graph to get new outputs with the same input. Variables allow us to add trainable parameters to a graph. They are constructed with a type and initial value:

W = tf.Variable([.3], dtype=tf.float32)
b = tf.Variable([-.3], dtype=tf.float32)
x = tf.placeholder(tf.float32)
linear_model = W*x + b

当你调用tf.constant时,常量被初始化,并且它们的值永远不会改变。 相反,变量在您调用tf.Variable时不会被初始化。 要初始化TensorFlow程序中的所有变量,您必须显式调用一个特殊的操作,如下所示:

Constants are initialized when you call tf.constant, and their value can never change. By contrast, variables are not initialized when you call tf.Variable. To initialize all the variables in a TensorFlow program, you must explicitly call a special operation as follows:

init = tf.global_variables_initializer()
sess.run(init)

实现init是TensorFlow子图的一个句柄,初始化所有的全局变量是非常重要的。 在我们调用sess.run之前,变量是未初始化的。

由于x是一个占位符,我们可以同时为x的多个值计算linear_model,如下所示:

It is important to realize init is a handle to the TensorFlow sub-graph that initializes all the global variables. Until we call sess.run, the variables are uninitialized.

Since x is a placeholder, we can evaluate linear_model for several values of x simultaneously as follows:

print(sess.run(linear_model, {x: [1, 2, 3, 4]}))

产生输出

to produce the output

[ 0.          0.30000001  0.60000002  0.90000004]

我们已经创建了一个模型,但我们不知道它有多好。 为了评估训练数据模型,我们需要一个占位符来提供所需的值,并且我们需要编写一个损失函数。

We've created a model, but we don't know how good it is yet. To evaluate the model on training data, we need a y placeholder to provide the desired values, and we need to write a loss function.

损失函数用于衡量当前模型距离提供的数据有多远。 我们将使用线性回归的标准损失模型,它将当前模型和提供的数据之间的差值的平方相加。 linear_model - y创建一个向量,其中每个元素是相应示例的误差增量。 我们调用tf.square来进行平方。 然后,我们求和所有的平方误差来创建一个标量,用·tf.reduce_sum`来获得所有例子的误差:

A loss function measures how far apart the current model is from the provided data. We'll use a standard loss model for linear regression, which sums the squares of the deltas between the current model and the provided data. linear_model - ycreates a vector where each element is the corresponding example's error delta. We call tf.square to square that error. Then, we sum all the squared errors to create a single scalar that abstracts the error of all examples using tf.reduce_sum:

y = tf.placeholder(tf.float32)
squared_deltas = tf.square(linear_model - y)
loss = tf.reduce_sum(squared_deltas)
print(sess.run(loss, {x: [1, 2, 3, 4], y: [0, -1, -2, -3]}))

产生损失价值

producing the loss value

23.66

我们可以通过将W和b的值重新赋值为-1和1的完美值来手动改进。变量初始化为提供给tf.Variable的值,但可以使用tf.assign等操作进行更改。 例如,W = -1和b = 1是我们模型的最佳参数。 我们可以相应地改变W和b:

We could improve this manually by reassigning the values of W and b to the perfect values of -1 and 1. A variable is initialized to the value provided to tf.Variable but can be changed using operations like tf.assign. For example, W=-1 and b=1 are the optimal parameters for our model. We can change W and b accordingly:

fixW = tf.assign(W, [-1.])
fixb = tf.assign(b, [1.])
sess.run([fixW, fixb])
print(sess.run(loss, {x: [1, 2, 3, 4], y: [0, -1, -2, -3]}))

最后的打印输出显示现在的损失是零。

The final print shows the loss now is zero.

0.0

我们猜测了W和b的“完美”值,但是机器学习的重点是自动找到正确的模型参数。 我们将在下一节展示如何完成这个。

We guessed the "perfect" values of W and b, but the whole point of machine learning is to find the correct model parameters automatically. We will show how to accomplish this in the next section.

tf.train API

机器学习的完整讨论超出了本教程的范围。 但是,TensorFlow提供了优化器,可以逐渐改变每个变量,以最大限度地减少损失函数。 最简单的优化器是梯度下降。 它根据相对于该变量的损失导数的大小来修改每个变量。 一般来说,手动计算符号派生是繁琐和容易出错的。 因此,只要使用函数tf.gradients描述模型,TensorFlow就可以自动生成衍生物。 为了简单起见,优化程序通常会为您执行此操作。 例如,

A complete discussion of machine learning is out of the scope of this tutorial. However, TensorFlow provides optimizers that slowly change each variable in order to minimize the loss function. The simplest optimizer is gradient descent. It modifies each variable according to the magnitude of the derivative of loss with respect to that variable. In general, computing symbolic derivatives manually is tedious and error-prone. Consequently, TensorFlow can automatically produce derivatives given only a description of the model using the function tf.gradients. For simplicity, optimizers typically do this for you. For example,

optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)
sess.run(init) # reset values to incorrect defaults.
for i in range(1000):
  sess.run(train, {x: [1, 2, 3, 4], y: [0, -1, -2, -3]})

print(sess.run([W, b]))

最终的模型参数:

results in the final model parameters:

[array([-0.9999969], dtype=float32), array([ 0.99999082], dtype=float32)]

现在我们已经完成了机器学习! 虽然这个简单的线性回归模型不需要太多的TensorFlow核心代码,但是更复杂的模型和方法将数据提供给模型需要更多的代码。 因此,TensorFlow为常见的模式,结构和功能提供更高层次的抽象。 我们将在下一节学习如何使用这些抽象。

Now we have done actual machine learning! Although this simple linear regression model does not require much TensorFlow core code, more complicated models and methods to feed data into your models necessitate more code. Thus, TensorFlow provides higher level abstractions for common patterns, structures, and functionality. We will learn how to use some of these abstractions in the next section.

完整的程序(Complete program)

完成的可训练线性回归模型如下所示:

The completed trainable linear regression model is shown here:

import tensorflow as tf

# Model parameters
W = tf.Variable([.3], dtype=tf.float32)
b = tf.Variable([-.3], dtype=tf.float32)
# Model input and output
x = tf.placeholder(tf.float32)
linear_model = W*x + b
y = tf.placeholder(tf.float32)

# loss
loss = tf.reduce_sum(tf.square(linear_model - y)) # sum of the squares
# optimizer
optimizer = tf.train.GradientDescentOptimizer(0.01)
train = optimizer.minimize(loss)

# training data
x_train = [1, 2, 3, 4]
y_train = [0, -1, -2, -3]
# training loop
init = tf.global_variables_initializer()
sess = tf.Session()
sess.run(init) # reset values to wrong
for i in range(1000):
  sess.run(train, {x: x_train, y: y_train})

# evaluate training accuracy
curr_W, curr_b, curr_loss = sess.run([W, b, loss], {x: x_train, y: y_train})
print("W: %s b: %s loss: %s"%(curr_W, curr_b, curr_loss))

当运行时,它将生成:

When run, it produces

W: [-0.9999969] b: [ 0.99999082] loss: 5.69997e-11

请注意,损失是非常小的数字(非常接近零)。 如果你运行这个程序,你的损失可能与上述损失不完全一样,因为模型是用伪随机值初始化的。

Notice that the loss is a very small number (very close to zero). If you run this program, your loss may not be exactly the same as the aforementioned loss because the model is initialized with pseudorandom values.

这个更复杂的程序仍然可以在TensorBoard中可视化

This more complicated program can still be visualized in TensorBoard

tf.estimator

tf.estimator是一个高级的TensorFlow库,它简化了机器学习的机制,包括:

  • 运行训练循环
  • 运行评估循环
  • 管理数据集

tf.estimator定义了许多常见的模型。

tf.estimator is a high-level TensorFlow library that simplifies the mechanics of machine learning, including the following:

  • running training loops
  • running evaluation loops
  • managing data sets
    tf.estimator defines many common models.

基本用法(Basic usage)

注意线性回归程序在tf.estimator中变得简单多了:

Notice how much simpler the linear regression program becomes with tf.estimator:

# NumPy is often used to load, manipulate and preprocess data.
import numpy as np
import tensorflow as tf

# Declare list of features. We only have one numeric feature. There are many
# other types of columns that are more complicated and useful.
feature_columns = [tf.feature_column.numeric_column("x", shape=[1])]

# An estimator is the front end to invoke training (fitting) and evaluation
# (inference). There are many predefined types like linear regression,
# linear classification, and many neural network classifiers and regressors.
# The following code provides an estimator that does linear regression.
estimator = tf.estimator.LinearRegressor(feature_columns=feature_columns)

# TensorFlow provides many helper methods to read and set up data sets.
# Here we use two data sets: one for training and one for evaluation
# We have to tell the function how many batches
# of data (num_epochs) we want and how big each batch should be.
x_train = np.array([1., 2., 3., 4.])
y_train = np.array([0., -1., -2., -3.])
x_eval = np.array([2., 5., 8., 1.])
y_eval = np.array([-1.01, -4.1, -7, 0.])
input_fn = tf.estimator.inputs.numpy_input_fn(
    {"x": x_train}, y_train, batch_size=4, num_epochs=None, shuffle=True)
train_input_fn = tf.estimator.inputs.numpy_input_fn(
    {"x": x_train}, y_train, batch_size=4, num_epochs=1000, shuffle=False)
eval_input_fn = tf.estimator.inputs.numpy_input_fn(
    {"x": x_eval}, y_eval, batch_size=4, num_epochs=1000, shuffle=False)

# We can invoke 1000 training steps by invoking the  method and passing the
# training data set.
estimator.train(input_fn=input_fn, steps=1000)

# Here we evaluate how well our model did.
train_metrics = estimator.evaluate(input_fn=train_input_fn)
eval_metrics = estimator.evaluate(input_fn=eval_input_fn)
print("train metrics: %r"% train_metrics)
print("eval metrics: %r"% eval_metrics)

当运行时,它产生像下面的输出:

When run, it produces something like

train metrics: {'average_loss': 1.4833182e-08, 'global_step': 1000, 'loss': 5.9332727e-08}
eval metrics: {'average_loss': 0.0025353201, 'global_step': 1000, 'loss': 0.01014128}

请注意我们的评估数据是如何有更高的损失,但仍然接近于零。 这意味着我们正在正确地学习。

train metrics: {'average_loss': 1.4833182e-08, 'global_step': 1000, 'loss': 5.9332727e-08}
eval metrics: {'average_loss': 0.0025353201, 'global_step': 1000, 'loss': 0.01014128}

自定义模型(A custom model)

tf.estimator不会将您锁定到其预定义的模型中。 假设我们想创建一个没有内置到TensorFlow中的自定义模型。 我们仍然可以保留高级抽象的数据集,喂食,训练等。 为了说明,我们将展示如何使用我们对低级别TensorFlow API的知识来实现我们自己的LinearRegressor的等效模型。

tf.estimator does not lock you into its predefined models. Suppose we wanted to create a custom model that is not built into TensorFlow. We can still retain the high level abstraction of data set, feeding, training, etc. of tf.estimator. For illustration, we will show how to implement our own equivalent model to LinearRegressor using our knowledge of the lower level TensorFlow API.

要定义一个与tf.estimator一起工作的自定义模型,我们需要使用tf.estimator.Estimator。 tf.estimator.LinearRegressor实际上是tf.estimator.Estimator的一个子类。 我们只需要提供一个函数model_fn来告诉tf.estimator如何评估预测,训练步骤和损失,而不是Estimator的子分类。 代码如下:

To define a custom model that works with tf.estimator, we need to use tf.estimator.Estimator. tf.estimator.LinearRegressor is actually a sub-class of tf.estimator.Estimator. Instead of sub-classing Estimator, we simply provide Estimator a function model_fn that tells tf.estimator how it can evaluate predictions, training steps, and loss. The code is as follows:

import numpy as np
import tensorflow as tf

# Declare list of features, we only have one real-valued feature
def model_fn(features, labels, mode):
  # Build a linear model and predict values
  W = tf.get_variable("W", [1], dtype=tf.float64)
  b = tf.get_variable("b", [1], dtype=tf.float64)
  y = W*features['x'] + b
  # Loss sub-graph
  loss = tf.reduce_sum(tf.square(y - labels))
  # Training sub-graph
  global_step = tf.train.get_global_step()
  optimizer = tf.train.GradientDescentOptimizer(0.01)
  train = tf.group(optimizer.minimize(loss),
                   tf.assign_add(global_step, 1))
  # EstimatorSpec connects subgraphs we built to the
  # appropriate functionality.
  return tf.estimator.EstimatorSpec(
      mode=mode,
      predictions=y,
      loss=loss,
      train_op=train)

estimator = tf.estimator.Estimator(model_fn=model_fn)
# define our data sets
x_train = np.array([1., 2., 3., 4.])
y_train = np.array([0., -1., -2., -3.])
x_eval = np.array([2., 5., 8., 1.])
y_eval = np.array([-1.01, -4.1, -7., 0.])
input_fn = tf.estimator.inputs.numpy_input_fn(
    {"x": x_train}, y_train, batch_size=4, num_epochs=None, shuffle=True)
train_input_fn = tf.estimator.inputs.numpy_input_fn(
    {"x": x_train}, y_train, batch_size=4, num_epochs=1000, shuffle=False)
eval_input_fn = tf.estimator.inputs.numpy_input_fn(
    {"x": x_eval}, y_eval, batch_size=4, num_epochs=1000, shuffle=False)

# train
estimator.train(input_fn=input_fn, steps=1000)
# Here we evaluate how well our model did.
train_metrics = estimator.evaluate(input_fn=train_input_fn)
eval_metrics = estimator.evaluate(input_fn=eval_input_fn)
print("train metrics: %r"% train_metrics)
print("eval metrics: %r"% eval_metrics)

运行时产生

When run, it produces

train metrics: {'loss': 1.227995e-11, 'global_step': 1000}
eval metrics: {'loss': 0.01010036, 'global_step': 1000}

请注意,自定义的model_fn()函数的内容与来自较低级别API的手动模型训练循环非常相似。

Notice how the contents of the custom model_fn() function are very similar to our manual model training loop from the lower level API.

下一步(Next steps)

现在您已经掌握了TensorFlow的基础知识。 我们还有更多的教程可供您了解更多信息。 如果您是机器学习的初学者,请参阅MNIST初学者,否则请参阅Deep MNIST专家。

Now you have a working knowledge of the basics of TensorFlow. We have several more tutorials that you can look at to learn more. If you are a beginner in machine learning see MNIST for beginners, otherwise see Deep MNIST for experts.

https://www.tensorflow.org/get_started/get_started#the_computational_graph

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,194评论 6 490
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,058评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,780评论 0 346
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,388评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,430评论 5 384
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,764评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,907评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,679评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,122评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,459评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,605评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,270评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,867评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,734评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,961评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,297评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,472评论 2 348